From 1046ece216dfd899e31c9476cc1886d6a34c2a4b Mon Sep 17 00:00:00 2001 From: niksedk Date: Wed, 26 Aug 2015 22:42:40 +0200 Subject: [PATCH] libse - initial commit to separate UI and logic --- build.bat | 4 + libse/BluRaySup/BluRaySupPalette.cs | 470 ++ libse/BluRaySup/BluRaySupParser.cs | 810 +++ libse/BluRaySup/BluRaySupPicture.cs | 572 ++ libse/BluRaySup/Core.cs | 90 + libse/BluRaySup/ImageObject.cs | 60 + libse/BluRaySup/ImageObjectFragment.cs | 37 + libse/BluRaySup/PaletteInfo.cs | 50 + libse/BluRaySup/ToolBox.cs | 122 + libse/BmpReader.cs | 22 + libse/Configuration.cs | 230 + libse/ContainerFormats/AviRiffData.cs | 147 + libse/ContainerFormats/Ebml/Element.cs | 53 + libse/ContainerFormats/Ebml/ElementId.cs | 43 + .../MaterialExchangeFormat/KeyIdentifier.cs | 16 + .../MaterialExchangeFormat/KlvPacket.cs | 155 + .../MaterialExchangeFormat/MxfParser.cs | 129 + .../MaterialExchangeFormat/PartitionStatus.cs | 11 + .../ContainerFormats/Matroska/MatroskaFile.cs | 649 ++ .../Matroska/MatroskaSubtitle.cs | 41 + .../Matroska/MatroskaTrackInfo.cs | 19 + libse/ContainerFormats/Mp4/Boxes/Box.cs | 82 + libse/ContainerFormats/Mp4/Boxes/Mdhd.cs | 489 ++ libse/ContainerFormats/Mp4/Boxes/Mdia.cs | 70 + libse/ContainerFormats/Mp4/Boxes/Minf.cs | 27 + libse/ContainerFormats/Mp4/Boxes/Moov.cs | 29 + libse/ContainerFormats/Mp4/Boxes/Mvhd.cs | 25 + libse/ContainerFormats/Mp4/Boxes/Stbl.cs | 201 + libse/ContainerFormats/Mp4/Boxes/Tkhd.cs | 41 + libse/ContainerFormats/Mp4/Boxes/Trak.cs | 29 + libse/ContainerFormats/Mp4/Mp4Parser.cs | 168 + libse/ContainerFormats/RiffDecodeHeader.cs | 452 ++ libse/ContainerFormats/RiffParser.cs | 492 ++ libse/DetectEncoding/EncodingTools.cs | 474 ++ .../Multilang/CMLangConvertCharset.cs | 9 + .../Multilang/CMLangConvertCharsetClass.cs | 24 + .../DetectEncoding/Multilang/CMLangString.cs | 9 + .../Multilang/CMLangStringClass.cs | 67 + .../Multilang/CMultiLanguage.cs | 9 + .../Multilang/CMultiLanguageClass.cs | 217 + .../DetectEncoding/Multilang/IEnumCodePage.cs | 18 + .../DetectEncoding/Multilang/IEnumRfc1766.cs | 18 + libse/DetectEncoding/Multilang/IEnumScript.cs | 18 + .../Multilang/IMLangCodePages.cs | 18 + .../Multilang/IMLangConvertCharset.cs | 24 + .../Multilang/IMLangFontLink.cs | 29 + .../Multilang/IMLangFontLink2.cs | 37 + .../Multilang/IMLangLineBreakConsole.cs | 16 + .../DetectEncoding/Multilang/IMLangString.cs | 19 + .../Multilang/IMLangStringAStr.cs | 39 + .../Multilang/IMLangStringBufA.cs | 21 + .../Multilang/IMLangStringBufW.cs | 21 + .../Multilang/IMLangStringWStr.cs | 39 + .../Multilang/IMultiLanguage.cs | 40 + .../Multilang/IMultiLanguage2.cs | 74 + .../Multilang/IMultiLanguage3.cs | 92 + .../Multilang/ISequentialStream.cs | 15 + libse/DetectEncoding/Multilang/IStream.cs | 37 + libse/DetectEncoding/Multilang/_FILETIME.cs | 11 + .../Multilang/_LARGE_INTEGER.cs | 10 + .../Multilang/_RemotableHandle.cs | 11 + .../Multilang/_ULARGE_INTEGER.cs | 10 + .../Multilang/__MIDL_IWinTypes_0009.cs | 13 + .../Multilang/tagDetectEncodingInfo.cs | 15 + .../DetectEncoding/Multilang/tagMIMECONTF.cs | 20 + .../DetectEncoding/Multilang/tagMIMECPINFO.cs | 25 + .../Multilang/tagMIMECSETINFO.cs | 13 + libse/DetectEncoding/Multilang/tagMLCPF.cs | 35 + .../DetectEncoding/Multilang/tagMLDETECTCP.cs | 23 + .../Multilang/tagMLSTR_FLAGS.cs | 9 + .../Multilang/tagRFC1766INFO.cs | 14 + .../Multilang/tagSCRIPFONTINFO.cs | 12 + .../DetectEncoding/Multilang/tagSCRIPTINFO.cs | 17 + libse/DetectEncoding/Multilang/tagSTATSTG.cs | 22 + .../Multilang/tagUNICODERANGE.cs | 11 + libse/Dictionaries/NamesList.cs | 258 + libse/Dictionaries/OcrFixReplaceList.cs | 962 +++ libse/Enums/FindType.cs | 9 + libse/Enums/SelectionChoice.cs | 9 + libse/Enums/SpellCheckAction.cs | 15 + libse/Enums/SubtitleSortCriteria.cs | 17 + libse/FastBitmap.cs | 110 + libse/FastFileStream.cs | 104 + libse/FileUtil.cs | 331 + libse/FindReplaceDialogHelper.cs | 232 + libse/Forms/CheckForUpdatesHelper.cs | 186 + libse/Forms/FixCommonErrorsHelper.cs | 464 ++ libse/Forms/RemoveTextForHI.cs | 1122 ++++ libse/Forms/RemoveTextForHISettings.cs | 48 + libse/Forms/SplitLongLinesHelper.cs | 143 + libse/Fourier.cs | 162 + libse/HistoryItem.cs | 52 + libse/HtmlUtil.cs | 669 ++ libse/IfoParser.cs | 619 ++ libse/ImageSplitter.cs | 691 ++ libse/ImageSplitterItem.cs | 27 + libse/Language.cs | 2501 ++++++++ libse/LanguageDeserializer.cs | 5663 +++++++++++++++++ libse/LanguageStructure.cs | 2326 +++++++ libse/LibSE.csproj | 447 ++ libse/ManagedBitmap.cs | 211 + libse/MurMurHash3.cs | 88 + libse/NativeMethods.cs | 131 + libse/NikseBitmap.cs | 1122 ++++ libse/NoBreakAfterItem.cs | 56 + libse/Paragraph.cs | 154 + libse/Properties/AssemblyInfo.cs | 36 + libse/RegistryUtil.cs | 43 + libse/Settings.cs | 3216 ++++++++++ libse/SpellCheckWord.cs | 8 + libse/SsaStyle.cs | 205 + libse/StringExtensions.cs | 167 + libse/StripableText.cs | 285 + libse/Subtitle.cs | 530 ++ libse/SubtitleFormats/AQTitle.cs | 149 + libse/SubtitleFormats/AbcIViewer.cs | 118 + .../SubtitleFormats/AdobeAfterEffectsFTME.cs | 103 + libse/SubtitleFormats/AdobeEncore.cs | 140 + .../AdobeEncoreLineTabNewLine.cs | 152 + libse/SubtitleFormats/AdobeEncoreLineTabs.cs | 142 + libse/SubtitleFormats/AdobeEncoreTabs.cs | 107 + .../AdobeEncoreWithLineNumbers.cs | 115 + .../AdobeEncoreWithLineNumbersNtsc.cs | 113 + .../AdvancedSubStationAlpha.cs | 1477 +++++ libse/SubtitleFormats/AvidCaption.cs | 141 + libse/SubtitleFormats/AvidDvd.cs | 154 + libse/SubtitleFormats/AvidStl.cs | 192 + libse/SubtitleFormats/Ayato.cs | 121 + libse/SubtitleFormats/BdnXml.cs | 130 + libse/SubtitleFormats/BelleNuitSubtitler.cs | 342 + libse/SubtitleFormats/CapMakerPlus.cs | 293 + libse/SubtitleFormats/CaptionAssistant.cs | 159 + libse/SubtitleFormats/Captionate.cs | 175 + libse/SubtitleFormats/CaptionateMs.cs | 155 + libse/SubtitleFormats/CaptionsInc.cs | 246 + libse/SubtitleFormats/CaraokeXml.cs | 112 + libse/SubtitleFormats/Cavena890.cs | 903 +++ libse/SubtitleFormats/CheetahCaption.cs | 357 ++ libse/SubtitleFormats/Chk.cs | 257 + libse/SubtitleFormats/Csv.cs | 97 + libse/SubtitleFormats/Csv2.cs | 131 + libse/SubtitleFormats/Csv3.cs | 194 + libse/SubtitleFormats/DCSubtitle.cs | 737 +++ libse/SubtitleFormats/DCinemaSmpte2007.cs | 779 +++ libse/SubtitleFormats/DCinemaSmpte2010.cs | 788 +++ libse/SubtitleFormats/DigiBeta.cs | 98 + libse/SubtitleFormats/Dost.cs | 114 + libse/SubtitleFormats/DvdStudioPro.cs | 182 + libse/SubtitleFormats/DvdStudioProSpace.cs | 104 + libse/SubtitleFormats/DvdStudioProSpaceOne.cs | 104 + libse/SubtitleFormats/DvdSubtitle.cs | 148 + libse/SubtitleFormats/DvdSubtitleSystem.cs | 104 + .../SubtitleFormats/ELRStudioClosedCaption.cs | 148 + libse/SubtitleFormats/Ebu.cs | 1138 ++++ libse/SubtitleFormats/Eeg708.cs | 128 + libse/SubtitleFormats/F4Rtf.cs | 69 + libse/SubtitleFormats/F4Text.cs | 162 + libse/SubtitleFormats/F4Xml.cs | 76 + libse/SubtitleFormats/FLVCoreCuePoints.cs | 157 + libse/SubtitleFormats/FabSubtitler.cs | 106 + libse/SubtitleFormats/FilmEditXml.cs | 177 + libse/SubtitleFormats/FinalCutProImage.cs | 104 + libse/SubtitleFormats/FinalCutProTest2Xml.cs | 296 + libse/SubtitleFormats/FinalCutProTextXml.cs | 366 ++ libse/SubtitleFormats/FinalCutProXCM.cs | 157 + libse/SubtitleFormats/FinalCutProXXml.cs | 191 + libse/SubtitleFormats/FinalCutProXml.cs | 617 ++ libse/SubtitleFormats/FinalCutProXml13.cs | 210 + libse/SubtitleFormats/FinalCutProXml14.cs | 197 + libse/SubtitleFormats/FinalCutProXml14Text.cs | 198 + libse/SubtitleFormats/FinalCutProXmlGap.cs | 163 + libse/SubtitleFormats/FlashXml.cs | 187 + libse/SubtitleFormats/Footage.cs | 168 + libse/SubtitleFormats/GpacTtxt.cs | 133 + libse/SubtitleFormats/HtmlSamiArray.cs | 106 + libse/SubtitleFormats/Idx.cs | 135 + .../SubtitleFormats/ImageLogicAutocaption.cs | 159 + libse/SubtitleFormats/IssXml.cs | 215 + libse/SubtitleFormats/ItunesTimedText.cs | 320 + libse/SubtitleFormats/Json.cs | 292 + libse/SubtitleFormats/JsonType2.cs | 91 + libse/SubtitleFormats/JsonType3.cs | 94 + libse/SubtitleFormats/JsonType4.cs | 99 + libse/SubtitleFormats/JsonType5.cs | 166 + libse/SubtitleFormats/JsonType6.cs | 183 + libse/SubtitleFormats/Lrc.cs | 229 + libse/SubtitleFormats/MPlayer2.cs | 161 + libse/SubtitleFormats/MacCaption.cs | 228 + libse/SubtitleFormats/MicroDvd.cs | 566 ++ libse/SubtitleFormats/MidwayInscriberCGX.cs | 118 + libse/SubtitleFormats/NciCaption.cs | 258 + .../SubtitleFormats/NciTimedRollUpCaptions.cs | 165 + libse/SubtitleFormats/OpenDvt.cs | 196 + libse/SubtitleFormats/Oresme.cs | 137 + libse/SubtitleFormats/OresmeDocXDocument.cs | 289 + libse/SubtitleFormats/PE2.cs | 163 + libse/SubtitleFormats/PListCaption.cs | 170 + libse/SubtitleFormats/Pac.cs | 1796 ++++++ libse/SubtitleFormats/PacUnicode.cs | 216 + libse/SubtitleFormats/PinnacleImpression.cs | 124 + libse/SubtitleFormats/Pns.cs | 163 + libse/SubtitleFormats/QubeMasterImport.cs | 150 + libse/SubtitleFormats/QuickTimeText.cs | 150 + libse/SubtitleFormats/RealTime.cs | 112 + libse/SubtitleFormats/RhozetHarmonic.cs | 190 + libse/SubtitleFormats/Sami.cs | 441 ++ libse/SubtitleFormats/SamiModern.cs | 19 + libse/SubtitleFormats/SamiYouTube.cs | 19 + libse/SubtitleFormats/SatBoxPng.cs | 98 + libse/SubtitleFormats/Scenarist.cs | 110 + .../ScenaristClosedCaptions.cs | 1790 ++++++ .../ScenaristClosedCaptionsDropFrame.cs | 23 + libse/SubtitleFormats/SmilTimesheetData.cs | 217 + libse/SubtitleFormats/SoftNiColonSub.cs | 243 + libse/SubtitleFormats/SoftNiSub.cs | 244 + libse/SubtitleFormats/Son.cs | 110 + .../SubtitleFormats/SonicScenaristBitmaps.cs | 116 + libse/SubtitleFormats/SonyDVDArchitect.cs | 108 + .../SonyDVDArchitectExplicitDuration.cs | 114 + .../SonyDVDArchitectLineAndDuration.cs | 131 + libse/SubtitleFormats/SonyDVDArchitectTabs.cs | 110 + .../SonyDVDArchitectWithLineNumbers.cs | 142 + libse/SubtitleFormats/Spruce.cs | 211 + libse/SubtitleFormats/SpruceWithSpace.cs | 145 + libse/SubtitleFormats/Spt.cs | 181 + libse/SubtitleFormats/StructuredTitles.cs | 147 + libse/SubtitleFormats/SubRip.cs | 278 + libse/SubtitleFormats/SubStationAlpha.cs | 525 ++ libse/SubtitleFormats/SubViewer10.cs | 142 + libse/SubtitleFormats/SubViewer20.cs | 161 + .../SubtitleFormats/SubtitleEditorProject.cs | 168 + libse/SubtitleFormats/SubtitleFormat.cs | 400 ++ libse/SubtitleFormats/SwiftInterchange2.cs | 159 + libse/SubtitleFormats/SwiftText.cs | 184 + .../SubtitleFormats/SwiftTextLineNoAndDur.cs | 181 + libse/SubtitleFormats/SwiftTextLineNumber.cs | 171 + libse/SubtitleFormats/TMPlayer.cs | 130 + libse/SubtitleFormats/TSB4.cs | 92 + libse/SubtitleFormats/Tek.cs | 123 + libse/SubtitleFormats/TextST.cs | 1138 ++++ libse/SubtitleFormats/TimeXml.cs | 110 + libse/SubtitleFormats/TimeXml2.cs | 125 + libse/SubtitleFormats/TimedText.cs | 291 + libse/SubtitleFormats/TimedText10.cs | 888 +++ libse/SubtitleFormats/TimedText200604.cs | 259 + libse/SubtitleFormats/TimedText200604CData.cs | 17 + libse/SubtitleFormats/TimelineAscii.cs | 214 + libse/SubtitleFormats/TimelineFootageAscii.cs | 212 + libse/SubtitleFormats/TimelineMvt.cs | 175 + libse/SubtitleFormats/TitleExchangePro.cs | 139 + libse/SubtitleFormats/Titra.cs | 144 + libse/SubtitleFormats/TmpegEncAW5.cs | 82 + libse/SubtitleFormats/TmpegEncText.cs | 107 + libse/SubtitleFormats/TmpegEncXml.cs | 385 ++ libse/SubtitleFormats/Tmx14.cs | 150 + libse/SubtitleFormats/TranscriberXml.cs | 141 + libse/SubtitleFormats/TurboTitler.cs | 106 + libse/SubtitleFormats/UTSubtitleXml.cs | 114 + libse/SubtitleFormats/UleadSubtitleFormat.cs | 139 + libse/SubtitleFormats/Ultech130.cs | 469 ++ .../UniversalSubtitleFormat.cs | 149 + libse/SubtitleFormats/UnknownSubtitle1.cs | 117 + libse/SubtitleFormats/UnknownSubtitle10.cs | 122 + libse/SubtitleFormats/UnknownSubtitle11.cs | 272 + libse/SubtitleFormats/UnknownSubtitle12.cs | 107 + libse/SubtitleFormats/UnknownSubtitle13.cs | 127 + libse/SubtitleFormats/UnknownSubtitle14.cs | 116 + libse/SubtitleFormats/UnknownSubtitle15.cs | 126 + libse/SubtitleFormats/UnknownSubtitle16.cs | 62 + libse/SubtitleFormats/UnknownSubtitle17.cs | 197 + libse/SubtitleFormats/UnknownSubtitle18.cs | 170 + libse/SubtitleFormats/UnknownSubtitle19.cs | 134 + libse/SubtitleFormats/UnknownSubtitle2.cs | 153 + libse/SubtitleFormats/UnknownSubtitle20.cs | 154 + libse/SubtitleFormats/UnknownSubtitle21.cs | 157 + libse/SubtitleFormats/UnknownSubtitle22.cs | 121 + libse/SubtitleFormats/UnknownSubtitle23.cs | 157 + libse/SubtitleFormats/UnknownSubtitle24.cs | 77 + libse/SubtitleFormats/UnknownSubtitle25.cs | 128 + libse/SubtitleFormats/UnknownSubtitle26.cs | 144 + libse/SubtitleFormats/UnknownSubtitle27.cs | 132 + libse/SubtitleFormats/UnknownSubtitle28.cs | 131 + libse/SubtitleFormats/UnknownSubtitle29.cs | 107 + libse/SubtitleFormats/UnknownSubtitle3.cs | 90 + libse/SubtitleFormats/UnknownSubtitle30.cs | 171 + libse/SubtitleFormats/UnknownSubtitle31.cs | 161 + libse/SubtitleFormats/UnknownSubtitle32.cs | 161 + libse/SubtitleFormats/UnknownSubtitle33.cs | 154 + libse/SubtitleFormats/UnknownSubtitle34.cs | 133 + libse/SubtitleFormats/UnknownSubtitle35.cs | 107 + libse/SubtitleFormats/UnknownSubtitle36.cs | 122 + libse/SubtitleFormats/UnknownSubtitle37.cs | 77 + libse/SubtitleFormats/UnknownSubtitle38.cs | 110 + libse/SubtitleFormats/UnknownSubtitle39.cs | 77 + libse/SubtitleFormats/UnknownSubtitle4.cs | 143 + libse/SubtitleFormats/UnknownSubtitle40.cs | 104 + libse/SubtitleFormats/UnknownSubtitle41.cs | 128 + libse/SubtitleFormats/UnknownSubtitle42.cs | 122 + libse/SubtitleFormats/UnknownSubtitle43.cs | 179 + libse/SubtitleFormats/UnknownSubtitle44.cs | 152 + libse/SubtitleFormats/UnknownSubtitle45.cs | 148 + libse/SubtitleFormats/UnknownSubtitle46.cs | 109 + libse/SubtitleFormats/UnknownSubtitle47.cs | 103 + libse/SubtitleFormats/UnknownSubtitle48.cs | 101 + libse/SubtitleFormats/UnknownSubtitle49.cs | 167 + libse/SubtitleFormats/UnknownSubtitle5.cs | 109 + libse/SubtitleFormats/UnknownSubtitle50.cs | 178 + libse/SubtitleFormats/UnknownSubtitle51.cs | 111 + libse/SubtitleFormats/UnknownSubtitle52.cs | 148 + libse/SubtitleFormats/UnknownSubtitle53.cs | 117 + libse/SubtitleFormats/UnknownSubtitle54.cs | 150 + libse/SubtitleFormats/UnknownSubtitle55.cs | 165 + libse/SubtitleFormats/UnknownSubtitle56.cs | 137 + libse/SubtitleFormats/UnknownSubtitle57.cs | 107 + libse/SubtitleFormats/UnknownSubtitle58.cs | 160 + libse/SubtitleFormats/UnknownSubtitle59.cs | 184 + libse/SubtitleFormats/UnknownSubtitle6.cs | 133 + libse/SubtitleFormats/UnknownSubtitle60.cs | 162 + libse/SubtitleFormats/UnknownSubtitle61.cs | 155 + libse/SubtitleFormats/UnknownSubtitle62.cs | 135 + libse/SubtitleFormats/UnknownSubtitle63.cs | 144 + libse/SubtitleFormats/UnknownSubtitle64.cs | 167 + libse/SubtitleFormats/UnknownSubtitle65.cs | 128 + libse/SubtitleFormats/UnknownSubtitle66.cs | 148 + libse/SubtitleFormats/UnknownSubtitle67.cs | 117 + libse/SubtitleFormats/UnknownSubtitle68.cs | 191 + libse/SubtitleFormats/UnknownSubtitle69.cs | 116 + libse/SubtitleFormats/UnknownSubtitle7.cs | 154 + libse/SubtitleFormats/UnknownSubtitle70.cs | 565 ++ libse/SubtitleFormats/UnknownSubtitle71.cs | 217 + libse/SubtitleFormats/UnknownSubtitle72.cs | 134 + libse/SubtitleFormats/UnknownSubtitle73.cs | 133 + libse/SubtitleFormats/UnknownSubtitle74.cs | 146 + libse/SubtitleFormats/UnknownSubtitle75.cs | 146 + libse/SubtitleFormats/UnknownSubtitle76.cs | 114 + libse/SubtitleFormats/UnknownSubtitle77.cs | 99 + libse/SubtitleFormats/UnknownSubtitle78.cs | 230 + libse/SubtitleFormats/UnknownSubtitle8.cs | 117 + libse/SubtitleFormats/UnknownSubtitle9.cs | 111 + libse/SubtitleFormats/Utx.cs | 111 + libse/SubtitleFormats/UtxFrames.cs | 104 + libse/SubtitleFormats/VocapiaSplit.cs | 170 + libse/SubtitleFormats/WebVTT.cs | 201 + .../WebVTTFileWithLineNumber.cs | 217 + libse/SubtitleFormats/Wsb.cs | 112 + libse/SubtitleFormats/Xif.cs | 237 + libse/SubtitleFormats/YouTubeAnnotations.cs | 249 + libse/SubtitleFormats/YouTubeSbv.cs | 184 + libse/SubtitleFormats/YouTubeTranscript.cs | 105 + .../YouTubeTranscriptOneLine.cs | 96 + libse/SubtitleFormats/ZeroG.cs | 102 + libse/TarHeader.cs | 40 + libse/TarReader.cs | 57 + libse/TaskbarList.cs | 234 + libse/TextDraw.cs | 139 + libse/TimeCode.cs | 224 + libse/TransportStream/AdaptationField.cs | 108 + .../TransportStream/ClutDefinitionSegment.cs | 49 + .../DisplayDefinitionSegment.cs | 30 + libse/TransportStream/DvbSubPes.cs | 395 ++ libse/TransportStream/EbuPesDataField.cs | 22 + libse/TransportStream/EbuPesDataFieldText.cs | 10 + libse/TransportStream/Helper.cs | 330 + libse/TransportStream/HummingDecoder.cs | 49 + libse/TransportStream/ObjectDataSegment.cs | 468 ++ libse/TransportStream/Packet.cs | 143 + .../PacketizedElementaryStream.cs | 96 + .../TransportStream/PageCompositionSegemnt.cs | 33 + .../PageCompositionSegmentRegion.cs | 10 + .../ProgramAssociationTable.cs | 43 + .../TransportStream/RegionClutSegmentEntry.cs | 59 + .../RegionCompositionSegment.cs | 52 + .../RegionCompositionSegmentObject.cs | 13 + libse/TransportStream/SubtitleSegment.cs | 83 + .../TransportStream/TransportStreamParser.cs | 443 ++ .../TransportStreamSubtitle.cs | 103 + libse/UknownFormatImporter.cs | 691 ++ libse/Utilities.cs | 3036 +++++++++ libse/VideoInfo.cs | 15 + libse/VobSub/Helper.cs | 325 + libse/VobSub/Idx.cs | 107 + libse/VobSub/IdxParagraph.cs | 17 + libse/VobSub/Mpeg2Header.cs | 36 + libse/VobSub/PacketizedElementaryStream.cs | 113 + libse/VobSub/SpHeader.cs | 31 + libse/VobSub/SubPicture.cs | 511 ++ libse/VobSub/VobSubMergedPack.cs | 21 + libse/VobSub/VobSubPack.cs | 35 + libse/VobSub/VobSubParser.cs | 247 + libse/VobSub/VobSubWriter.cs | 444 ++ libse/Wave.cs | 18 + libse/WaveToVisualizer.cs | 638 ++ libse/XSub.cs | 121 + libse/XmlDeserializerGenerator.cs | 105 + libse/ZipExtractor.cs | 440 ++ src/Controls/AudioVisualizer.cs | 3 +- src/Controls/SubtitleListView.cs | 13 +- src/Controls/TimeUpDown.cs | 11 +- src/Controls/VideoPlayerContainer.cs | 3 +- src/Forms/About.cs | 2 +- src/Forms/AddToNames.cs | 4 +- src/Forms/AddToOcrReplaceList.cs | 4 +- src/Forms/AddToUserDic.cs | 6 +- src/Forms/AddWaveForm.cs | 6 +- src/Forms/AddWaveformBatch.cs | 7 +- src/Forms/AdjustDisplayDuration.cs | 4 +- src/Forms/AlignmentPicker.cs | 4 +- src/Forms/ApplyDurationLimits.cs | 5 +- src/Forms/AutoBreakUnbreakLines.cs | 7 +- src/Forms/BatchConvert.cs | 13 +- src/Forms/Beamer.cs | 1 - src/Forms/ChangeCasing.cs | 3 +- src/Forms/ChangeCasingNames.cs | 3 +- src/Forms/ChangeFrameRate.cs | 7 +- src/Forms/ChangeSpeedInPercent.cs | 4 +- src/Forms/CheckForUpdates.cs | 4 +- src/Forms/ChooseAudioTrack.cs | 4 +- src/Forms/ChooseEncoding.cs | 4 +- src/Forms/ChooseLanguage.cs | 4 +- src/Forms/ChooseResolution.cs | 7 +- src/Forms/ChooseStyle.cs | 6 +- src/Forms/ColorChooser.cs | 2 +- src/Forms/ColumnPaste.cs | 4 +- src/Forms/Compare.cs | 3 +- src/Forms/DCinema/DCinemaPropertiesInterop.cs | 1 + src/Forms/DCinema/DCinemaPropertiesSmpte.cs | 1 + src/Forms/DialogDoNotShowAgain.cs | 2 +- src/Forms/DoNotBreakAfterListEdit.cs | 4 +- src/Forms/DurationsBridgeGaps.cs | 4 +- src/Forms/DvdSubRip.cs | 5 +- src/Forms/DvdSubRipChooseLanguage.cs | 8 +- src/Forms/EbuSaveOptions.cs | 3 +- src/Forms/EffectKaraoke.cs | 5 +- src/Forms/EffectTypewriter.cs | 1 + src/Forms/ExportCustomText.cs | 2 +- src/Forms/ExportCustomTextFormat.cs | 6 +- src/Forms/ExportPngXml.cs | 17 +- src/Forms/ExportPngXmlPreview.cs | 4 +- src/Forms/ExportText.cs | 5 +- src/Forms/ExportTextST.cs | 10 +- src/Forms/ExtractDateTimeInfo.cs | 9 +- src/Forms/FcpProperties.cs | 4 +- src/Forms/FindDialog.cs | 6 +- src/Forms/FindSubtitleLine.cs | 7 +- src/Forms/FixCommonErrors.cs | 7 +- src/Forms/GetDictionaries.cs | 2 +- src/Forms/GetTesseractDictionaries.cs | 4 +- src/Forms/GoToLine.cs | 4 +- src/Forms/GoogleOrMicrosoftTranslate.cs | 4 +- src/Forms/GoogleTranslate.cs | 1 - src/Forms/HardSubExtract.cs | 11 +- src/Forms/ImportImages.cs | 2 +- src/Forms/ImportSceneChanges.cs | 5 +- src/Forms/ImportText.cs | 5 +- src/Forms/ImportUnknownFormat.cs | 4 +- src/Forms/Interjections.cs | 4 +- src/Forms/JoinSubtitles.cs | 6 +- src/Forms/Main.cs | 56 +- src/Forms/MatroskaSubtitleChooser.cs | 6 +- src/Forms/MeasurementConverter.cs | 4 +- src/Forms/MergeDoubleLines.cs | 5 +- src/Forms/MergeShortLines.cs | 5 +- src/Forms/MergeTextWithSameTimeCodes.cs | 2 +- src/Forms/ModifySelection.cs | 1 - src/Forms/MultipleReplace.cs | 1 - src/Forms/NetworkChat.cs | 7 +- src/Forms/NetworkJoin.cs | 4 +- src/Forms/NetworkLogAndInfo.cs | 4 +- src/Forms/NetworkStart.cs | 4 +- src/Forms/NuendoProperties.cs | 4 +- src/Forms/OCRSpellCheck.cs | 5 +- src/Forms/OpenVideoDvd.cs | 1 - src/Forms/PacEncoding.cs | 6 +- src/Forms/PluginsGet.cs | 3 +- src/Forms/RemoveTextFromHearImpaired.cs | 4 +- src/Forms/ReplaceDialog.cs | 6 +- src/Forms/RestoreAutoBackup.cs | 4 +- src/Forms/SeekSilence.cs | 4 +- .../SetMinimumDisplayTimeBetweenParagraphs.cs | 6 +- src/Forms/SetSyncPoint.cs | 14 +- src/Forms/SetVideoOffset.cs | 2 +- src/Forms/Settings.cs | 14 +- src/Forms/ShowEarlierLater.cs | 6 +- src/Forms/ShowHistory.cs | 6 +- src/Forms/SpellCheck.cs | 7 +- src/Forms/Split.cs | 6 +- src/Forms/SplitLongLines.cs | 3 +- src/Forms/SplitSubtitle.cs | 9 +- src/Forms/StartNumberingFrom.cs | 4 +- src/Forms/Statistics.cs | 7 +- src/Forms/StatusLog.cs | 4 +- src/Forms/Styles/StylesForm.cs | 3 +- src/Forms/Styles/SubStationAlphaStyles.cs | 3 +- src/Forms/Styles/TimedTextStyles.cs | 4 +- src/Forms/SubStationAlphaProperties.cs | 10 +- src/Forms/SyncPointsSync.cs | 4 +- src/Forms/TimedTextProperties.cs | 6 +- src/Forms/TransportStreamSubtitleChooser.cs | 7 +- src/Forms/UnknownSubtitle.cs | 4 +- src/Forms/VideoControlsUndocked.cs | 4 +- src/Forms/VideoError.cs | 4 +- src/Forms/VideoPlayerUndocked.cs | 4 +- src/Forms/VisualSync.cs | 22 +- src/Forms/VobSubEditCharacters.cs | 7 +- src/Forms/VobSubNOcrCharacterInspect.cs | 3 +- src/Forms/VobSubNOcrEdit.cs | 2 +- src/Forms/VobSubNOcrTrain.cs | 2 +- src/Forms/VobSubOcr.cs | 18 +- src/Forms/VobSubOcrCharacter.cs | 6 +- src/Forms/VobSubOcrCharacterInspect.cs | 6 +- src/Forms/VobSubOcrNOcrCharacter.cs | 6 +- src/Forms/VobSubOcrNewFolder.cs | 5 +- src/Forms/VobSubOcrSetItalicFactor.cs | 4 +- src/Forms/Watermark.cs | 11 +- src/Forms/WaveformGenerateTimeCodes.cs | 4 +- src/Forms/WaveformUndocked.cs | 4 +- src/Forms/WebVttNewVoice.cs | 5 +- src/Forms/YouTubeAnnotationsImport.cs | 5 +- src/Logic/CommandLineConvert.cs | 11 +- src/Logic/Dictionaries/OcrFixReplaceList.cs | 1 + src/Logic/Forms/FixCommonErrorsHelper.cs | 3 + src/Logic/Forms/RemoveTextForHISettings.cs | 1 + src/Logic/Forms/SplitLongLinesHelper.cs | 1 + .../Networking/NikseWebServiceSession.cs | 2 + src/Logic/NikseBitmapImageSplitter.cs | 2 + src/Logic/OCR/Binary/BinaryOcrBitmap.cs | 11 +- src/Logic/OCR/NOcrDb.cs | 2 + src/Logic/OCR/OcrFixEngine.cs | 2 +- src/Logic/SpellCheck/Hunspell.cs | 1 + src/Logic/UiEbuSaveHelper.cs | 40 + src/Logic/UiGetPacEncoding.cs | 24 + src/Logic/UiGetYouTubeAnnotationStyles.cs | 30 + src/Logic/UiUtil.cs | 248 + src/Logic/VideoPlayers/LibVlcMono.cs | 1 + src/Logic/VideoPlayers/MPlayer.cs | 1 + src/Logic/VideoPlayers/QuartsPlayer.cs | 2 + src/SubtitleEdit.csproj | 407 +- src/SubtitleEdit.sln | 8 +- src/Test/FixCommonErrorsTest.cs | 5 +- .../Logic/BluRaySup/BluRaySupParserTest.cs | 2 +- src/Test/Logic/BluRaySup/ToolBoxTest.cs | 2 +- src/Test/Logic/Dictionaries/NamesListTest.cs | 2 +- .../Dictionaries/OcrFixReplaceListTest.cs | 2 +- .../Forms/RemoveTextForHearImpairedTest.cs | 3 +- src/Test/Logic/LanguageTest.cs | 2 +- src/Test/Logic/Mp4/Mp4Test.cs | 2 +- src/Test/Logic/Ocr/BinaryOcrTest.cs | 2 +- src/Test/Logic/ParagraphTest.cs | 2 +- src/Test/Logic/StripableTextTest.cs | 1 + .../SubtitleFormats/SubtitleFormatsTest.cs | 3 +- src/Test/Logic/TarFileTest.cs | 2 +- src/Test/Logic/TimeCodeTest.cs | 2 +- .../TransportStream/TransportStreamTest.cs | 3 +- src/Test/Logic/UtilitiesTest.cs | 2 +- src/Test/Logic/VideoFormats/MatroskaTest.cs | 2 +- src/Test/Logic/VobSub/VobSubTest.cs | 4 +- src/Test/Test.csproj | 4 + 557 files changed, 90414 insertions(+), 777 deletions(-) create mode 100644 libse/BluRaySup/BluRaySupPalette.cs create mode 100644 libse/BluRaySup/BluRaySupParser.cs create mode 100644 libse/BluRaySup/BluRaySupPicture.cs create mode 100644 libse/BluRaySup/Core.cs create mode 100644 libse/BluRaySup/ImageObject.cs create mode 100644 libse/BluRaySup/ImageObjectFragment.cs create mode 100644 libse/BluRaySup/PaletteInfo.cs create mode 100644 libse/BluRaySup/ToolBox.cs create mode 100644 libse/BmpReader.cs create mode 100644 libse/Configuration.cs create mode 100644 libse/ContainerFormats/AviRiffData.cs create mode 100644 libse/ContainerFormats/Ebml/Element.cs create mode 100644 libse/ContainerFormats/Ebml/ElementId.cs create mode 100644 libse/ContainerFormats/MaterialExchangeFormat/KeyIdentifier.cs create mode 100644 libse/ContainerFormats/MaterialExchangeFormat/KlvPacket.cs create mode 100644 libse/ContainerFormats/MaterialExchangeFormat/MxfParser.cs create mode 100644 libse/ContainerFormats/MaterialExchangeFormat/PartitionStatus.cs create mode 100644 libse/ContainerFormats/Matroska/MatroskaFile.cs create mode 100644 libse/ContainerFormats/Matroska/MatroskaSubtitle.cs create mode 100644 libse/ContainerFormats/Matroska/MatroskaTrackInfo.cs create mode 100644 libse/ContainerFormats/Mp4/Boxes/Box.cs create mode 100644 libse/ContainerFormats/Mp4/Boxes/Mdhd.cs create mode 100644 libse/ContainerFormats/Mp4/Boxes/Mdia.cs create mode 100644 libse/ContainerFormats/Mp4/Boxes/Minf.cs create mode 100644 libse/ContainerFormats/Mp4/Boxes/Moov.cs create mode 100644 libse/ContainerFormats/Mp4/Boxes/Mvhd.cs create mode 100644 libse/ContainerFormats/Mp4/Boxes/Stbl.cs create mode 100644 libse/ContainerFormats/Mp4/Boxes/Tkhd.cs create mode 100644 libse/ContainerFormats/Mp4/Boxes/Trak.cs create mode 100644 libse/ContainerFormats/Mp4/Mp4Parser.cs create mode 100644 libse/ContainerFormats/RiffDecodeHeader.cs create mode 100644 libse/ContainerFormats/RiffParser.cs create mode 100644 libse/DetectEncoding/EncodingTools.cs create mode 100644 libse/DetectEncoding/Multilang/CMLangConvertCharset.cs create mode 100644 libse/DetectEncoding/Multilang/CMLangConvertCharsetClass.cs create mode 100644 libse/DetectEncoding/Multilang/CMLangString.cs create mode 100644 libse/DetectEncoding/Multilang/CMLangStringClass.cs create mode 100644 libse/DetectEncoding/Multilang/CMultiLanguage.cs create mode 100644 libse/DetectEncoding/Multilang/CMultiLanguageClass.cs create mode 100644 libse/DetectEncoding/Multilang/IEnumCodePage.cs create mode 100644 libse/DetectEncoding/Multilang/IEnumRfc1766.cs create mode 100644 libse/DetectEncoding/Multilang/IEnumScript.cs create mode 100644 libse/DetectEncoding/Multilang/IMLangCodePages.cs create mode 100644 libse/DetectEncoding/Multilang/IMLangConvertCharset.cs create mode 100644 libse/DetectEncoding/Multilang/IMLangFontLink.cs create mode 100644 libse/DetectEncoding/Multilang/IMLangFontLink2.cs create mode 100644 libse/DetectEncoding/Multilang/IMLangLineBreakConsole.cs create mode 100644 libse/DetectEncoding/Multilang/IMLangString.cs create mode 100644 libse/DetectEncoding/Multilang/IMLangStringAStr.cs create mode 100644 libse/DetectEncoding/Multilang/IMLangStringBufA.cs create mode 100644 libse/DetectEncoding/Multilang/IMLangStringBufW.cs create mode 100644 libse/DetectEncoding/Multilang/IMLangStringWStr.cs create mode 100644 libse/DetectEncoding/Multilang/IMultiLanguage.cs create mode 100644 libse/DetectEncoding/Multilang/IMultiLanguage2.cs create mode 100644 libse/DetectEncoding/Multilang/IMultiLanguage3.cs create mode 100644 libse/DetectEncoding/Multilang/ISequentialStream.cs create mode 100644 libse/DetectEncoding/Multilang/IStream.cs create mode 100644 libse/DetectEncoding/Multilang/_FILETIME.cs create mode 100644 libse/DetectEncoding/Multilang/_LARGE_INTEGER.cs create mode 100644 libse/DetectEncoding/Multilang/_RemotableHandle.cs create mode 100644 libse/DetectEncoding/Multilang/_ULARGE_INTEGER.cs create mode 100644 libse/DetectEncoding/Multilang/__MIDL_IWinTypes_0009.cs create mode 100644 libse/DetectEncoding/Multilang/tagDetectEncodingInfo.cs create mode 100644 libse/DetectEncoding/Multilang/tagMIMECONTF.cs create mode 100644 libse/DetectEncoding/Multilang/tagMIMECPINFO.cs create mode 100644 libse/DetectEncoding/Multilang/tagMIMECSETINFO.cs create mode 100644 libse/DetectEncoding/Multilang/tagMLCPF.cs create mode 100644 libse/DetectEncoding/Multilang/tagMLDETECTCP.cs create mode 100644 libse/DetectEncoding/Multilang/tagMLSTR_FLAGS.cs create mode 100644 libse/DetectEncoding/Multilang/tagRFC1766INFO.cs create mode 100644 libse/DetectEncoding/Multilang/tagSCRIPFONTINFO.cs create mode 100644 libse/DetectEncoding/Multilang/tagSCRIPTINFO.cs create mode 100644 libse/DetectEncoding/Multilang/tagSTATSTG.cs create mode 100644 libse/DetectEncoding/Multilang/tagUNICODERANGE.cs create mode 100644 libse/Dictionaries/NamesList.cs create mode 100644 libse/Dictionaries/OcrFixReplaceList.cs create mode 100644 libse/Enums/FindType.cs create mode 100644 libse/Enums/SelectionChoice.cs create mode 100644 libse/Enums/SpellCheckAction.cs create mode 100644 libse/Enums/SubtitleSortCriteria.cs create mode 100644 libse/FastBitmap.cs create mode 100644 libse/FastFileStream.cs create mode 100644 libse/FileUtil.cs create mode 100644 libse/FindReplaceDialogHelper.cs create mode 100644 libse/Forms/CheckForUpdatesHelper.cs create mode 100644 libse/Forms/FixCommonErrorsHelper.cs create mode 100644 libse/Forms/RemoveTextForHI.cs create mode 100644 libse/Forms/RemoveTextForHISettings.cs create mode 100644 libse/Forms/SplitLongLinesHelper.cs create mode 100644 libse/Fourier.cs create mode 100644 libse/HistoryItem.cs create mode 100644 libse/HtmlUtil.cs create mode 100644 libse/IfoParser.cs create mode 100644 libse/ImageSplitter.cs create mode 100644 libse/ImageSplitterItem.cs create mode 100644 libse/Language.cs create mode 100644 libse/LanguageDeserializer.cs create mode 100644 libse/LanguageStructure.cs create mode 100644 libse/LibSE.csproj create mode 100644 libse/ManagedBitmap.cs create mode 100644 libse/MurMurHash3.cs create mode 100644 libse/NativeMethods.cs create mode 100644 libse/NikseBitmap.cs create mode 100644 libse/NoBreakAfterItem.cs create mode 100644 libse/Paragraph.cs create mode 100644 libse/Properties/AssemblyInfo.cs create mode 100644 libse/RegistryUtil.cs create mode 100644 libse/Settings.cs create mode 100644 libse/SpellCheckWord.cs create mode 100644 libse/SsaStyle.cs create mode 100644 libse/StringExtensions.cs create mode 100644 libse/StripableText.cs create mode 100644 libse/Subtitle.cs create mode 100644 libse/SubtitleFormats/AQTitle.cs create mode 100644 libse/SubtitleFormats/AbcIViewer.cs create mode 100644 libse/SubtitleFormats/AdobeAfterEffectsFTME.cs create mode 100644 libse/SubtitleFormats/AdobeEncore.cs create mode 100644 libse/SubtitleFormats/AdobeEncoreLineTabNewLine.cs create mode 100644 libse/SubtitleFormats/AdobeEncoreLineTabs.cs create mode 100644 libse/SubtitleFormats/AdobeEncoreTabs.cs create mode 100644 libse/SubtitleFormats/AdobeEncoreWithLineNumbers.cs create mode 100644 libse/SubtitleFormats/AdobeEncoreWithLineNumbersNtsc.cs create mode 100644 libse/SubtitleFormats/AdvancedSubStationAlpha.cs create mode 100644 libse/SubtitleFormats/AvidCaption.cs create mode 100644 libse/SubtitleFormats/AvidDvd.cs create mode 100644 libse/SubtitleFormats/AvidStl.cs create mode 100644 libse/SubtitleFormats/Ayato.cs create mode 100644 libse/SubtitleFormats/BdnXml.cs create mode 100644 libse/SubtitleFormats/BelleNuitSubtitler.cs create mode 100644 libse/SubtitleFormats/CapMakerPlus.cs create mode 100644 libse/SubtitleFormats/CaptionAssistant.cs create mode 100644 libse/SubtitleFormats/Captionate.cs create mode 100644 libse/SubtitleFormats/CaptionateMs.cs create mode 100644 libse/SubtitleFormats/CaptionsInc.cs create mode 100644 libse/SubtitleFormats/CaraokeXml.cs create mode 100644 libse/SubtitleFormats/Cavena890.cs create mode 100644 libse/SubtitleFormats/CheetahCaption.cs create mode 100644 libse/SubtitleFormats/Chk.cs create mode 100644 libse/SubtitleFormats/Csv.cs create mode 100644 libse/SubtitleFormats/Csv2.cs create mode 100644 libse/SubtitleFormats/Csv3.cs create mode 100644 libse/SubtitleFormats/DCSubtitle.cs create mode 100644 libse/SubtitleFormats/DCinemaSmpte2007.cs create mode 100644 libse/SubtitleFormats/DCinemaSmpte2010.cs create mode 100644 libse/SubtitleFormats/DigiBeta.cs create mode 100644 libse/SubtitleFormats/Dost.cs create mode 100644 libse/SubtitleFormats/DvdStudioPro.cs create mode 100644 libse/SubtitleFormats/DvdStudioProSpace.cs create mode 100644 libse/SubtitleFormats/DvdStudioProSpaceOne.cs create mode 100644 libse/SubtitleFormats/DvdSubtitle.cs create mode 100644 libse/SubtitleFormats/DvdSubtitleSystem.cs create mode 100644 libse/SubtitleFormats/ELRStudioClosedCaption.cs create mode 100644 libse/SubtitleFormats/Ebu.cs create mode 100644 libse/SubtitleFormats/Eeg708.cs create mode 100644 libse/SubtitleFormats/F4Rtf.cs create mode 100644 libse/SubtitleFormats/F4Text.cs create mode 100644 libse/SubtitleFormats/F4Xml.cs create mode 100644 libse/SubtitleFormats/FLVCoreCuePoints.cs create mode 100644 libse/SubtitleFormats/FabSubtitler.cs create mode 100644 libse/SubtitleFormats/FilmEditXml.cs create mode 100644 libse/SubtitleFormats/FinalCutProImage.cs create mode 100644 libse/SubtitleFormats/FinalCutProTest2Xml.cs create mode 100644 libse/SubtitleFormats/FinalCutProTextXml.cs create mode 100644 libse/SubtitleFormats/FinalCutProXCM.cs create mode 100644 libse/SubtitleFormats/FinalCutProXXml.cs create mode 100644 libse/SubtitleFormats/FinalCutProXml.cs create mode 100644 libse/SubtitleFormats/FinalCutProXml13.cs create mode 100644 libse/SubtitleFormats/FinalCutProXml14.cs create mode 100644 libse/SubtitleFormats/FinalCutProXml14Text.cs create mode 100644 libse/SubtitleFormats/FinalCutProXmlGap.cs create mode 100644 libse/SubtitleFormats/FlashXml.cs create mode 100644 libse/SubtitleFormats/Footage.cs create mode 100644 libse/SubtitleFormats/GpacTtxt.cs create mode 100644 libse/SubtitleFormats/HtmlSamiArray.cs create mode 100644 libse/SubtitleFormats/Idx.cs create mode 100644 libse/SubtitleFormats/ImageLogicAutocaption.cs create mode 100644 libse/SubtitleFormats/IssXml.cs create mode 100644 libse/SubtitleFormats/ItunesTimedText.cs create mode 100644 libse/SubtitleFormats/Json.cs create mode 100644 libse/SubtitleFormats/JsonType2.cs create mode 100644 libse/SubtitleFormats/JsonType3.cs create mode 100644 libse/SubtitleFormats/JsonType4.cs create mode 100644 libse/SubtitleFormats/JsonType5.cs create mode 100644 libse/SubtitleFormats/JsonType6.cs create mode 100644 libse/SubtitleFormats/Lrc.cs create mode 100644 libse/SubtitleFormats/MPlayer2.cs create mode 100644 libse/SubtitleFormats/MacCaption.cs create mode 100644 libse/SubtitleFormats/MicroDvd.cs create mode 100644 libse/SubtitleFormats/MidwayInscriberCGX.cs create mode 100644 libse/SubtitleFormats/NciCaption.cs create mode 100644 libse/SubtitleFormats/NciTimedRollUpCaptions.cs create mode 100644 libse/SubtitleFormats/OpenDvt.cs create mode 100644 libse/SubtitleFormats/Oresme.cs create mode 100644 libse/SubtitleFormats/OresmeDocXDocument.cs create mode 100644 libse/SubtitleFormats/PE2.cs create mode 100644 libse/SubtitleFormats/PListCaption.cs create mode 100644 libse/SubtitleFormats/Pac.cs create mode 100644 libse/SubtitleFormats/PacUnicode.cs create mode 100644 libse/SubtitleFormats/PinnacleImpression.cs create mode 100644 libse/SubtitleFormats/Pns.cs create mode 100644 libse/SubtitleFormats/QubeMasterImport.cs create mode 100644 libse/SubtitleFormats/QuickTimeText.cs create mode 100644 libse/SubtitleFormats/RealTime.cs create mode 100644 libse/SubtitleFormats/RhozetHarmonic.cs create mode 100644 libse/SubtitleFormats/Sami.cs create mode 100644 libse/SubtitleFormats/SamiModern.cs create mode 100644 libse/SubtitleFormats/SamiYouTube.cs create mode 100644 libse/SubtitleFormats/SatBoxPng.cs create mode 100644 libse/SubtitleFormats/Scenarist.cs create mode 100644 libse/SubtitleFormats/ScenaristClosedCaptions.cs create mode 100644 libse/SubtitleFormats/ScenaristClosedCaptionsDropFrame.cs create mode 100644 libse/SubtitleFormats/SmilTimesheetData.cs create mode 100644 libse/SubtitleFormats/SoftNiColonSub.cs create mode 100644 libse/SubtitleFormats/SoftNiSub.cs create mode 100644 libse/SubtitleFormats/Son.cs create mode 100644 libse/SubtitleFormats/SonicScenaristBitmaps.cs create mode 100644 libse/SubtitleFormats/SonyDVDArchitect.cs create mode 100644 libse/SubtitleFormats/SonyDVDArchitectExplicitDuration.cs create mode 100644 libse/SubtitleFormats/SonyDVDArchitectLineAndDuration.cs create mode 100644 libse/SubtitleFormats/SonyDVDArchitectTabs.cs create mode 100644 libse/SubtitleFormats/SonyDVDArchitectWithLineNumbers.cs create mode 100644 libse/SubtitleFormats/Spruce.cs create mode 100644 libse/SubtitleFormats/SpruceWithSpace.cs create mode 100644 libse/SubtitleFormats/Spt.cs create mode 100644 libse/SubtitleFormats/StructuredTitles.cs create mode 100644 libse/SubtitleFormats/SubRip.cs create mode 100644 libse/SubtitleFormats/SubStationAlpha.cs create mode 100644 libse/SubtitleFormats/SubViewer10.cs create mode 100644 libse/SubtitleFormats/SubViewer20.cs create mode 100644 libse/SubtitleFormats/SubtitleEditorProject.cs create mode 100644 libse/SubtitleFormats/SubtitleFormat.cs create mode 100644 libse/SubtitleFormats/SwiftInterchange2.cs create mode 100644 libse/SubtitleFormats/SwiftText.cs create mode 100644 libse/SubtitleFormats/SwiftTextLineNoAndDur.cs create mode 100644 libse/SubtitleFormats/SwiftTextLineNumber.cs create mode 100644 libse/SubtitleFormats/TMPlayer.cs create mode 100644 libse/SubtitleFormats/TSB4.cs create mode 100644 libse/SubtitleFormats/Tek.cs create mode 100644 libse/SubtitleFormats/TextST.cs create mode 100644 libse/SubtitleFormats/TimeXml.cs create mode 100644 libse/SubtitleFormats/TimeXml2.cs create mode 100644 libse/SubtitleFormats/TimedText.cs create mode 100644 libse/SubtitleFormats/TimedText10.cs create mode 100644 libse/SubtitleFormats/TimedText200604.cs create mode 100644 libse/SubtitleFormats/TimedText200604CData.cs create mode 100644 libse/SubtitleFormats/TimelineAscii.cs create mode 100644 libse/SubtitleFormats/TimelineFootageAscii.cs create mode 100644 libse/SubtitleFormats/TimelineMvt.cs create mode 100644 libse/SubtitleFormats/TitleExchangePro.cs create mode 100644 libse/SubtitleFormats/Titra.cs create mode 100644 libse/SubtitleFormats/TmpegEncAW5.cs create mode 100644 libse/SubtitleFormats/TmpegEncText.cs create mode 100644 libse/SubtitleFormats/TmpegEncXml.cs create mode 100644 libse/SubtitleFormats/Tmx14.cs create mode 100644 libse/SubtitleFormats/TranscriberXml.cs create mode 100644 libse/SubtitleFormats/TurboTitler.cs create mode 100644 libse/SubtitleFormats/UTSubtitleXml.cs create mode 100644 libse/SubtitleFormats/UleadSubtitleFormat.cs create mode 100644 libse/SubtitleFormats/Ultech130.cs create mode 100644 libse/SubtitleFormats/UniversalSubtitleFormat.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle1.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle10.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle11.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle12.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle13.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle14.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle15.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle16.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle17.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle18.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle19.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle2.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle20.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle21.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle22.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle23.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle24.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle25.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle26.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle27.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle28.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle29.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle3.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle30.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle31.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle32.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle33.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle34.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle35.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle36.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle37.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle38.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle39.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle4.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle40.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle41.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle42.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle43.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle44.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle45.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle46.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle47.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle48.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle49.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle5.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle50.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle51.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle52.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle53.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle54.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle55.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle56.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle57.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle58.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle59.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle6.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle60.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle61.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle62.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle63.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle64.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle65.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle66.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle67.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle68.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle69.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle7.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle70.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle71.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle72.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle73.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle74.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle75.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle76.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle77.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle78.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle8.cs create mode 100644 libse/SubtitleFormats/UnknownSubtitle9.cs create mode 100644 libse/SubtitleFormats/Utx.cs create mode 100644 libse/SubtitleFormats/UtxFrames.cs create mode 100644 libse/SubtitleFormats/VocapiaSplit.cs create mode 100644 libse/SubtitleFormats/WebVTT.cs create mode 100644 libse/SubtitleFormats/WebVTTFileWithLineNumber.cs create mode 100644 libse/SubtitleFormats/Wsb.cs create mode 100644 libse/SubtitleFormats/Xif.cs create mode 100644 libse/SubtitleFormats/YouTubeAnnotations.cs create mode 100644 libse/SubtitleFormats/YouTubeSbv.cs create mode 100644 libse/SubtitleFormats/YouTubeTranscript.cs create mode 100644 libse/SubtitleFormats/YouTubeTranscriptOneLine.cs create mode 100644 libse/SubtitleFormats/ZeroG.cs create mode 100644 libse/TarHeader.cs create mode 100644 libse/TarReader.cs create mode 100644 libse/TaskbarList.cs create mode 100644 libse/TextDraw.cs create mode 100644 libse/TimeCode.cs create mode 100644 libse/TransportStream/AdaptationField.cs create mode 100644 libse/TransportStream/ClutDefinitionSegment.cs create mode 100644 libse/TransportStream/DisplayDefinitionSegment.cs create mode 100644 libse/TransportStream/DvbSubPes.cs create mode 100644 libse/TransportStream/EbuPesDataField.cs create mode 100644 libse/TransportStream/EbuPesDataFieldText.cs create mode 100644 libse/TransportStream/Helper.cs create mode 100644 libse/TransportStream/HummingDecoder.cs create mode 100644 libse/TransportStream/ObjectDataSegment.cs create mode 100644 libse/TransportStream/Packet.cs create mode 100644 libse/TransportStream/PacketizedElementaryStream.cs create mode 100644 libse/TransportStream/PageCompositionSegemnt.cs create mode 100644 libse/TransportStream/PageCompositionSegmentRegion.cs create mode 100644 libse/TransportStream/ProgramAssociationTable.cs create mode 100644 libse/TransportStream/RegionClutSegmentEntry.cs create mode 100644 libse/TransportStream/RegionCompositionSegment.cs create mode 100644 libse/TransportStream/RegionCompositionSegmentObject.cs create mode 100644 libse/TransportStream/SubtitleSegment.cs create mode 100644 libse/TransportStream/TransportStreamParser.cs create mode 100644 libse/TransportStream/TransportStreamSubtitle.cs create mode 100644 libse/UknownFormatImporter.cs create mode 100644 libse/Utilities.cs create mode 100644 libse/VideoInfo.cs create mode 100644 libse/VobSub/Helper.cs create mode 100644 libse/VobSub/Idx.cs create mode 100644 libse/VobSub/IdxParagraph.cs create mode 100644 libse/VobSub/Mpeg2Header.cs create mode 100644 libse/VobSub/PacketizedElementaryStream.cs create mode 100644 libse/VobSub/SpHeader.cs create mode 100644 libse/VobSub/SubPicture.cs create mode 100644 libse/VobSub/VobSubMergedPack.cs create mode 100644 libse/VobSub/VobSubPack.cs create mode 100644 libse/VobSub/VobSubParser.cs create mode 100644 libse/VobSub/VobSubWriter.cs create mode 100644 libse/Wave.cs create mode 100644 libse/WaveToVisualizer.cs create mode 100644 libse/XSub.cs create mode 100644 libse/XmlDeserializerGenerator.cs create mode 100644 libse/ZipExtractor.cs create mode 100644 src/Logic/UiEbuSaveHelper.cs create mode 100644 src/Logic/UiGetPacEncoding.cs create mode 100644 src/Logic/UiGetYouTubeAnnotationStyles.cs create mode 100644 src/Logic/UiUtil.cs diff --git a/build.bat b/build.bat index 7671e86b0..45d9a4545 100644 --- a/build.bat +++ b/build.bat @@ -46,6 +46,10 @@ TITLE %BUILDTYPE%ing SubtitleEdit - Release^|Any CPU... /maxcpucount /consoleloggerparameters:DisableMPLogging;Summary;Verbosity=minimal IF %ERRORLEVEL% NEQ 0 GOTO EndWithError +ECHO. +ECHO ILRepack... +"packages\ILRepack.2.0.1\tools\ILRepack.exe" /out:"bin\Release\SubtitleEdit.exe" "bin\Release\SubtitleEdit.exe" "bin\Release\libse.dll" "packages\NHunspell.1.2.5554.16953\lib\net\NHunspell.dll" "packages\zlib.net.1.0.4.0\lib\zlib.net.dll" "DLLs\Interop.QuartzTypeLib.dll" /targetplatform:v4 /internalize /parallel +ECHO. ECHO. POPD diff --git a/libse/BluRaySup/BluRaySupPalette.cs b/libse/BluRaySup/BluRaySupPalette.cs new file mode 100644 index 000000000..92a38e1cb --- /dev/null +++ b/libse/BluRaySup/BluRaySupPalette.cs @@ -0,0 +1,470 @@ +/* + * Copyright 2009 Volker Oth (0xdeadbeef) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * NOTE: Converted to C# and modified by Nikse.dk@gmail.com + */ + +namespace Nikse.SubtitleEdit.Core.BluRaySup +{ + public class BluRaySupPalette + { + /** Number of palette entries */ + private int size; + /** Byte buffer for RED info */ + private byte[] r; + /** Byte buffer for GREEN info */ + private byte[] g; + /** Byte buffer for BLUE info */ + private byte[] b; + /** Byte buffer for alpha info */ + private byte[] a; + /** Byte buffer for Y (luminance) info */ + private byte[] y; + /** Byte buffer for Cb (chrominance blue) info */ + private byte[] cb; + /** Byte buffer for Cr (chrominance red) info */ + private byte[] cr; + /** Use BT.601 color model instead of BT.709 */ + private bool useBT601; + + /** + * Convert YCBCr color info to RGB + * @param y 8 bit luminance + * @param cb 8 bit chrominance blue + * @param cr 8 bit chrominance red + * @return Integer array with red, blue, green component (in this order) + */ + public static int[] YCbCr2Rgb(int y, int cb, int cr, bool useBt601) + { + int[] rgb = new int[3]; + double r, g, b; + + y -= 16; + cb -= 128; + cr -= 128; + + var y1 = y * 1.164383562; + if (useBt601) + { + /* BT.601 for YCbCr 16..235 -> RGB 0..255 (PC) */ + r = y1 + cr * 1.596026317; + g = y1 - cr * 0.8129674985 - cb * 0.3917615979; + b = y1 + cb * 2.017232218; + } + else + { + /* BT.709 for YCbCr 16..235 -> RGB 0..255 (PC) */ + r = y1 + cr * 1.792741071; + g = y1 - cr * 0.5329093286 - cb * 0.2132486143; + b = y1 + cb * 2.112401786; + } + rgb[0] = (int)(r + 0.5); + rgb[1] = (int)(g + 0.5); + rgb[2] = (int)(b + 0.5); + for (int i = 0; i < 3; i++) + { + if (rgb[i] < 0) + rgb[i] = 0; + else if (rgb[i] > 255) + rgb[i] = 255; + } + return rgb; + } + + /** + * Convert RGB color info to YCBCr + * @param r 8 bit red component + * @param g 8 bit green component + * @param b 8 bit blue component + * @return Integer array with luminance (Y), chrominance blue (Cb), chrominance red (Cr) (in this order) + */ + private static int[] Rgb2YCbCr(int r, int g, int b, bool useBt601) + { + int[] yCbCr = new int[3]; + double y, cb, cr; + + if (useBt601) + { + /* BT.601 for RGB 0..255 (PC) -> YCbCr 16..235 */ + y = r * 0.299 * 219 / 255 + g * 0.587 * 219 / 255 + b * 0.114 * 219 / 255; + cb = -r * 0.168736 * 224 / 255 - g * 0.331264 * 224 / 255 + b * 0.5 * 224 / 255; + cr = r * 0.5 * 224 / 255 - g * 0.418688 * 224 / 255 - b * 0.081312 * 224 / 255; + } + else + { + /* BT.709 for RGB 0..255 (PC) -> YCbCr 16..235 */ + y = r * 0.2126 * 219 / 255 + g * 0.7152 * 219 / 255 + b * 0.0722 * 219 / 255; + cb = -r * 0.2126 / 1.8556 * 224 / 255 - g * 0.7152 / 1.8556 * 224 / 255 + b * 0.5 * 224 / 255; + cr = r * 0.5 * 224 / 255 - g * 0.7152 / 1.5748 * 224 / 255 - b * 0.0722 / 1.5748 * 224 / 255; + } + yCbCr[0] = 16 + (int)(y + 0.5); + yCbCr[1] = 128 + (int)(cb + 0.5); + yCbCr[2] = 128 + (int)(cr + 0.5); + for (int i = 0; i < 3; i++) + { + if (yCbCr[i] < 16) + yCbCr[i] = 16; + else + { + if (i == 0) + { + if (yCbCr[i] > 235) + yCbCr[i] = 235; + } + else + { + if (yCbCr[i] > 240) + yCbCr[i] = 240; + } + } + } + return yCbCr; + } + + /** + * Ctor - initializes palette with transparent black (RGBA: 0x00000000) + * @param palSize Number of palette entries + * @param use601 Use BT.601 instead of BT.709 + */ + public BluRaySupPalette(int palSize, bool use601) + { + size = palSize; + useBT601 = use601; + r = new byte[size]; + g = new byte[size]; + b = new byte[size]; + a = new byte[size]; + y = new byte[size]; + cb = new byte[size]; + cr = new byte[size]; + + // set at least all alpha values to invisible + int[] yCbCr = Rgb2YCbCr(0, 0, 0, useBT601); + for (int i = 0; i < palSize; i++) + { + a[i] = 0; + r[i] = 0; + g[i] = 0; + b[i] = 0; + y[i] = (byte)yCbCr[0]; + cb[i] = (byte)yCbCr[1]; + cr[i] = (byte)yCbCr[2]; + } + } + + /** + * Ctor - initializes palette with transparent black (RGBA: 0x00000000) + * @param palSize Number of palette entries + */ + public BluRaySupPalette(int palSize) + : this(palSize, false) + { + } + + /** + * Ctor - construct palette from red, green blue and alpha buffers + * @param red Byte buffer containing the red components + * @param green Byte buffer containing the green components + * @param blue Byte buffer containing the blue components + * @param alpha Byte buffer containing the alpha components + * @param use601 Use BT.601 instead of BT.709 + */ + public BluRaySupPalette(byte[] red, byte[] green, byte[] blue, byte[] alpha, bool use601) + { + size = red.Length; + useBT601 = use601; + r = new byte[size]; + g = new byte[size]; + b = new byte[size]; + a = new byte[size]; + y = new byte[size]; + cb = new byte[size]; + cr = new byte[size]; + + for (int i = 0; i < size; i++) + { + a[i] = alpha[i]; + r[i] = red[i]; + g[i] = green[i]; + b[i] = blue[i]; + var yCbCr = Rgb2YCbCr(r[i] & 0xff, g[i] & 0xff, b[i] & 0xff, useBT601); + y[i] = (byte)yCbCr[0]; + cb[i] = (byte)yCbCr[1]; + cr[i] = (byte)yCbCr[2]; + } + } + + /** + * Ctor - construct palette from red, green blue and alpha buffers + * @param red Byte buffer containing the red components + * @param green Byte buffer containing the green components + * @param blue Byte buffer containing the blue components + * @param alpha Byte buffer containing the alpha components + */ + public BluRaySupPalette(byte[] red, byte[] green, byte[] blue, byte[] alpha) + : this(red, green, blue, alpha, false) + { + } + + /** + * Ctor - construct new (independent) palette from existing one + * @param p Palette to copy values from + */ + public BluRaySupPalette(BluRaySupPalette p) + { + size = p.GetSize(); + useBT601 = p.UsesBt601(); + r = new byte[size]; + g = new byte[size]; + b = new byte[size]; + a = new byte[size]; + y = new byte[size]; + cb = new byte[size]; + cr = new byte[size]; + + for (int i = 0; i < size; i++) + { + a[i] = p.a[i]; + r[i] = p.r[i]; + g[i] = p.g[i]; + b[i] = p.b[i]; + y[i] = p.y[i]; + cb[i] = p.cb[i]; + cr[i] = p.cr[i]; + } + } + + /** + * Set palette index "index" to color "c" in ARGB format + * @param index Palette index + * @param c Color in ARGB format + */ + public void SetArgb(int index, int c) + { + int a1 = (c >> 24) & 0xff; + int r1 = (c >> 16) & 0xff; + int g1 = (c >> 8) & 0xff; + int b1 = c & 0xff; + SetRgb(index, r1, g1, b1); + SetAlpha(index, a1); + } + + /** + * Return palette entry at index as Integer in ARGB format + * @param index Palette index + * @return Palette entry at index as Integer in ARGB format + */ + public int GetArgb(int index) + { + return ((a[index] & 0xff) << 24) | ((r[index] & 0xff) << 16) | ((g[index] & 0xff) << 8) | (b[index] & 0xff); + } + + internal void SetColor(int index, System.Drawing.Color color) + { + SetRgb(index, color.R, color.G, color.B); + SetAlpha(index, color.A); + } + + /** + * Set palette entry (RGB mode) + * @param index Palette index + * @param red 8bit red component + * @param green 8bit green component + * @param blue 8bit blue component + */ + public void SetRgb(int index, int red, int green, int blue) + { + r[index] = (byte)red; + g[index] = (byte)green; + b[index] = (byte)blue; + // create yCbCr + int[] yCbCr = Rgb2YCbCr(red, green, blue, useBT601); + y[index] = (byte)yCbCr[0]; + cb[index] = (byte)yCbCr[1]; + cr[index] = (byte)yCbCr[2]; + } + + /** + * Set palette entry (YCbCr mode) + * @param index Palette index + * @param yn 8bit Y component + * @param cbn 8bit Cb component + * @param crn 8bit Cr component + */ + public void SetYCbCr(int index, int yn, int cbn, int crn) + { + y[index] = (byte)yn; + cb[index] = (byte)cbn; + cr[index] = (byte)crn; + // create RGB + int[] rgb = YCbCr2Rgb(yn, cbn, crn, useBT601); + r[index] = (byte)rgb[0]; + g[index] = (byte)rgb[1]; + b[index] = (byte)rgb[2]; + } + + /** + * Set alpha channel + * @param index Palette index + * @param alpha 8bit alpha channel value + */ + public void SetAlpha(int index, int alpha) + { + a[index] = (byte)alpha; + } + + /** + * Get alpha channel + * @param index Palette index + * @return 8bit alpha channel value + */ + public int GetAlpha(int index) + { + return a[index] & 0xff; + } + + /** + * Return byte array of alpha channel components + * @return Byte array of alpha channel components (don't modify!) + */ + public byte[] GetAlpha() + { + return a; + } + + /** + * Get Integer array containing 8bit red, green, blue components (in this order) + * @param index Palette index + * @return Integer array containing 8bit red, green, blue components (in this order) + */ + public int[] GetRgb(int index) + { + int[] rgb = new int[3]; + rgb[0] = r[index] & 0xff; + rgb[1] = g[index] & 0xff; + rgb[2] = b[index] & 0xff; + return rgb; + } + + /** + * Get Integer array containing 8bit Y, Cb, Cr components (in this order) + * @param index Palette index + * @return Integer array containing 8bit Y, Cb, Cr components (in this order) + */ + public int[] GetYCbCr(int index) + { + int[] yCbCr = new int[3]; + yCbCr[0] = y[index] & 0xff; + yCbCr[1] = cb[index] & 0xff; + yCbCr[2] = cr[index] & 0xff; + return yCbCr; + } + + /** + * Return byte array of red components + * @return Byte array of red components (don't modify!) + */ + public byte[] GetR() + { + return r; + } + + /** + * Return byte array of green components + * @return Byte array of green components (don't modify!) + */ + public byte[] GetG() + { + return g; + } + + /** + * Return byte array of blue components + * @return Byte array of blue components (don't modify!) + */ + public byte[] GetB() + { + return b; + } + + /** + * Return byte array of Y components + * @return Byte array of Y components (don't modify!) + */ + public byte[] GetY() + { + return y; + } + + /** + * Return byte array of Cb components + * @return Byte array of Cb components (don't modify!) + */ + public byte[] GetCb() + { + return cb; + } + + /** + * Return byte array of Cr components + * @return Byte array of Cr components (don't modify!) + */ + public byte[] GetCr() + { + return cr; + } + + /** + * Get size of palette (number of entries) + * @return Size of palette (number of entries) + */ + public int GetSize() + { + return size; + } + + /** + * Return index of most transparent palette entry or the index of the first completely transparent color + * @return Index of most transparent palette entry or the index of the first completely transparent color + */ + public int GetTransparentIndex() + { + // find (most) transparent index in palette + int transpIdx = 0; + int minAlpha = 0x100; + for (int i = 0; i < size; i++) + { + if ((a[i] & 0xff) < minAlpha) + { + minAlpha = a[i] & 0xff; + transpIdx = i; + if (minAlpha == 0) + break; + } + } + return transpIdx; + } + + /** + * Get: use of BT.601 color model instead of BT.709 + * @return True if BT.601 is used + */ + public bool UsesBt601() + { + return useBT601; + } + + } +} \ No newline at end of file diff --git a/libse/BluRaySup/BluRaySupParser.cs b/libse/BluRaySup/BluRaySupParser.cs new file mode 100644 index 000000000..c69906895 --- /dev/null +++ b/libse/BluRaySup/BluRaySupParser.cs @@ -0,0 +1,810 @@ +/* + * Copyright 2009 Volker Oth (0xdeadbeef) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * NOTE: Converted to C# and modified by Nikse.dk@gmail.com + */ + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Text; + +namespace Nikse.SubtitleEdit.Core.BluRaySup +{ + public static class BluRaySupParser + { + private class SupSegment + { + /// + /// Type of segment + /// + public int Type; + + /// + /// segment size in bytes + /// + public int Size; + + /// + /// segment PTS time stamp + /// + public long PtsTimestamp; + + /// + /// segment DTS time stamp + /// + public long DtsTimestamp; + } + + public class PcsObject + { + public int ObjectId; + public int WindowId; + public bool IsForced; + public Point Origin; + } + + public static class SupDecoder + { + private const int AlphaCrop = 14; + + public static BluRaySupPalette DecodePalette(IList paletteInfos) + { + BluRaySupPalette palette = new BluRaySupPalette(256); + // by definition, index 0xff is always completely transparent + // also all entries must be fully transparent after initialization + + bool fadeOut = false; + for (int j = 0; j < paletteInfos.Count; j++) + { + PaletteInfo p = paletteInfos[j]; + int index = 0; + + for (int i = 0; i < p.PaletteSize; i++) + { + // each palette entry consists of 5 bytes + int palIndex = p.PaletteBuffer[index]; + int y = p.PaletteBuffer[++index]; + int cr = p.PaletteBuffer[++index]; + int cb = p.PaletteBuffer[++index]; + int alpha = p.PaletteBuffer[++index]; + + int alphaOld = palette.GetAlpha(palIndex); + // avoid fading out + if (alpha >= alphaOld) + { + if (alpha < AlphaCrop) + {// to not mess with scaling algorithms, make transparent color black + y = 16; + cr = 128; + cb = 128; + } + palette.SetAlpha(palIndex, alpha); + } + else + { + fadeOut = true; + } + + palette.SetYCbCr(palIndex, y, cb, cr); + index++; + } + } + if (fadeOut) + { + System.Diagnostics.Debug.Print("fade out detected -> patched palette\n"); + } + return palette; + } + + /// + /// Decode caption from the input stream + /// + /// bitmap of the decoded caption + public static Bitmap DecodeImage(PcsObject pcs, IList data, List palettes) + { + long ticks = DateTime.Now.Ticks; + + if (pcs == null || data == null || data.Count == 0) + return new Bitmap(1, 1); + + int w = data[0].Size.Width; + int h = data[0].Size.Height; + + var bm = new FastBitmap(new Bitmap(w, h)); + bm.LockImage(); + BluRaySupPalette pal = DecodePalette(palettes); + + int index = 0; + int ofs = 0; + int xpos = 0; + + byte[] buf = data[0].Fragment.ImageBuffer; + index = 0; + do + { + int b = buf[index++] & 0xff; + if (b == 0 && index < buf.Length) + { + b = buf[index++] & 0xff; + if (b == 0) + { + // next line + ofs = (ofs / w) * w; + if (xpos < w) + ofs += w; + xpos = 0; + } + else + { + int size; + if ((b & 0xC0) == 0x40) + { + if (index < buf.Length) + { + // 00 4x xx -> xxx zeroes + size = ((b - 0x40) << 8) + (buf[index++] & 0xff); + Color c = Color.FromArgb(pal.GetArgb(0)); + for (int i = 0; i < size; i++) + PutPixel(bm, ofs++, c); + xpos += size; + } + } + else if ((b & 0xC0) == 0x80) + { + if (index < buf.Length) + { + // 00 8x yy -> x times value y + size = (b - 0x80); + b = buf[index++] & 0xff; + Color c = Color.FromArgb(pal.GetArgb(b)); + for (int i = 0; i < size; i++) + PutPixel(bm, ofs++, c); + xpos += size; + } + } + else if ((b & 0xC0) != 0) + { + if (index < buf.Length) + { + // 00 cx yy zz -> xyy times value z + size = ((b - 0xC0) << 8) + (buf[index++] & 0xff); + b = buf[index++] & 0xff; + Color c = Color.FromArgb(pal.GetArgb(b)); + for (int i = 0; i < size; i++) + PutPixel(bm, ofs++, c); + xpos += size; + } + } + else + { + // 00 xx -> xx times 0 + Color c = Color.FromArgb(pal.GetArgb(0)); + for (int i = 0; i < b; i++) + PutPixel(bm, ofs++, c); + xpos += b; + } + } + } + else + { + PutPixel(bm, ofs++, b, pal); + xpos++; + } + } while (index < buf.Length); + + bm.UnlockImage(); + return bm.GetBitmap(); + } + + private static void PutPixel(FastBitmap bmp, int index, int color, BluRaySupPalette palette) + { + int x = index % bmp.Width; + int y = index / bmp.Width; + if (x < bmp.Width && y < bmp.Height) + bmp.SetPixel(x, y, Color.FromArgb(palette.GetArgb(color))); + } + + private static void PutPixel(FastBitmap bmp, int index, Color color) + { + if (color.A > 0) + { + int x = index % bmp.Width; + int y = index / bmp.Width; + if (x < bmp.Width && y < bmp.Height) + bmp.SetPixel(x, y, color); + } + } + } + + public class PcsData + { + public int CompNum; + public CompositionState CompositionState; + public bool PaletteUpdate; + public long StartTime; // Pts + public long EndTime; // end Pts + public Size Size; + public int FramesPerSecondType; + public int PaletteId; + public List PcsObjects; + public string Message; + public List> BitmapObjects; + public List PaletteInfos; + + /// + /// if true, contains forced entry + /// + public bool IsForced + { + get + { + foreach (PcsObject obj in PcsObjects) + { + if (obj.IsForced) + return true; + } + return false; + } + } + + public Bitmap GetBitmap() + { + if (PcsObjects.Count == 1) + return SupDecoder.DecodeImage(PcsObjects[0], BitmapObjects[0], PaletteInfos); + + var r = Rectangle.Empty; + for (int ioIndex = 0; ioIndex < PcsObjects.Count; ioIndex++) + { + var ioRect = new Rectangle(PcsObjects[ioIndex].Origin, BitmapObjects[ioIndex][0].Size); + if (r.IsEmpty) + r = ioRect; + else + r = Rectangle.Union(r, ioRect); + } + var mergedBmp = new Bitmap(r.Width, r.Height, PixelFormat.Format32bppArgb); + for (var ioIndex = 0; ioIndex < PcsObjects.Count; ioIndex++) + { + var offset = PcsObjects[ioIndex].Origin - new Size(r.Location); + using (var singleBmp = SupDecoder.DecodeImage(PcsObjects[ioIndex], BitmapObjects[ioIndex], PaletteInfos)) + using (var gSideBySide = Graphics.FromImage(mergedBmp)) + { + gSideBySide.DrawImage(singleBmp, offset.X, offset.Y); + } + } + return mergedBmp; + } + } + + public class PdsData + { + public string Message; + public int PaletteId; + public int PaletteVersion; + public PaletteInfo PaletteInfo; + } + + public class OdsData + { + public int ObjectId; + public int ObjectVersion; + public string Message; + public bool IsFirst; + public Size Size; + public ImageObjectFragment Fragment; + } + + /// + /// PGS composition state + /// + public enum CompositionState + { + /// + /// Normal: doesn't have to be complete + /// + Normal, + + /// + /// Acquisition point + /// + AcquPoint, + + /// + /// Epoch start - clears the screen + /// + EpochStart, + + /// + /// Epoch continue + /// + EpochContinue, + + /// + /// Unknown value + /// + Invalid, + } + + private const int HeaderSize = 13; + + /// + /// Parses a Blu-ray sup file + /// + /// BluRay sup file name + /// Parsing info is logged here + /// List of BluRaySupPictures + public static List ParseBluRaySup(string fileName, StringBuilder log) + { + using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + return ParseBluRaySup(fs, log, false); + } + } + + private static SupSegment ParseSegmentHeader(byte[] buffer, StringBuilder log) + { + var segment = new SupSegment(); + if (buffer[0] == 0x50 && buffer[1] == 0x47) // 80 + 71 - P G + { + segment.PtsTimestamp = BigEndianInt32(buffer, 2); // read PTS + segment.DtsTimestamp = BigEndianInt32(buffer, 6); // read PTS + segment.Type = buffer[10]; + segment.Size = BigEndianInt16(buffer, 11); + } + else + { + log.AppendLine("Unable to read segment - PG missing!"); + } + return segment; + } + + private static SupSegment ParseSegmentHeaderFromMatroska(byte[] buffer) + { + var segment = new SupSegment(); + segment.Type = buffer[0]; + segment.Size = BigEndianInt16(buffer, 1); + return segment; + } + + /// + /// Parse an PCS packet which contains width/height info + /// + /// Raw data buffer, starting right after segment + private static PcsObject ParsePcs(byte[] buffer, int offset) + { + var pcs = new PcsObject(); + // composition_object: + pcs.ObjectId = BigEndianInt16(buffer, 11 + offset); // 16bit object_id_ref + pcs.WindowId = buffer[13 + offset]; + // skipped: 8bit window_id_ref + // object_cropped_flag: 0x80, forced_on_flag = 0x040, 6bit reserved + int forcedCropped = buffer[14 + offset]; + pcs.IsForced = ((forcedCropped & 0x40) == 0x40); + pcs.Origin = new Point(BigEndianInt16(buffer, 15 + offset), BigEndianInt16(buffer, 17 + offset)); + return pcs; + } + + private static PcsData ParsePicture(byte[] buffer, SupSegment segment) + { + var sb = new StringBuilder(); + var pcs = new PcsData(); + pcs.Size = new Size(BigEndianInt16(buffer, 0), BigEndianInt16(buffer, 2)); + pcs.FramesPerSecondType = buffer[4]; // hi nibble: frame_rate, lo nibble: reserved + pcs.CompNum = BigEndianInt16(buffer, 5); + pcs.CompositionState = GetCompositionState(buffer[7]); + pcs.StartTime = segment.PtsTimestamp; + // 8bit palette_update_flag (0x80), 7bit reserved + pcs.PaletteUpdate = (buffer[8] == 0x80); + pcs.PaletteId = buffer[9]; // 8bit palette_id_ref + int compositionObjectCount = buffer[10]; // 8bit number_of_composition_objects (0..2) + + sb.AppendFormat("CompNum: {0}, Pts: {1}, State: {2}, PalUpdate: {3}, PalId {4}", pcs.CompNum, ToolBox.PtsToTimeString(pcs.StartTime), pcs.CompositionState, pcs.PaletteUpdate, pcs.PaletteId); + + if (pcs.CompositionState == CompositionState.Invalid) + { + sb.Append("Illegal composition state Invalid"); + } + else + { + int offset = 0; + pcs.PcsObjects = new List(); + for (int compObjIndex = 0; compObjIndex < compositionObjectCount; compObjIndex++) + { + PcsObject pcsObj = ParsePcs(buffer, offset); + pcs.PcsObjects.Add(pcsObj); + sb.AppendLine(); + sb.AppendFormat("ObjId: {0}, WinId: {1}, Forced: {2}, X: {3}, Y: {4}", + pcsObj.ObjectId, pcsObj.WindowId, pcsObj.IsForced, pcsObj.Origin.X, pcsObj.Origin.Y); + offset += 8; + } + } + pcs.Message = sb.ToString(); + return pcs; + } + + private static bool CompletePcs(PcsData pcs, Dictionary> bitmapObjects, Dictionary> palettes) + { + if (pcs == null || pcs.PcsObjects == null || palettes == null) + return false; + + if (pcs.PcsObjects.Count == 0) + return true; + + if (!palettes.ContainsKey(pcs.PaletteId)) + return false; + + pcs.PaletteInfos = new List(palettes[pcs.PaletteId]); + pcs.BitmapObjects = new List>(); + for (int index = 0; index < pcs.PcsObjects.Count; index++) + { + int objId = pcs.PcsObjects[index].ObjectId; + if (!bitmapObjects.ContainsKey(objId)) + return false; + pcs.BitmapObjects.Add(bitmapObjects[objId]); + } + return true; + } + + /// + /// parse an PDS packet which contain palette info + /// + /// Buffer of raw byte data, starting right after segment + /// object containing info about the current segment + /// number of valid palette entries (-1 for fault) + private static PdsData ParsePds(byte[] buffer, SupSegment segment) + { + int paletteId = buffer[0]; // 8bit palette ID (0..7) + // 8bit palette version number (incremented for each palette change) + int paletteUpdate = buffer[1]; + + var p = new PaletteInfo(); + p.PaletteSize = (segment.Size - 2) / 5; + + if (p.PaletteSize <= 0) + return new PdsData { Message = "Empty palette" }; + + p.PaletteBuffer = new byte[p.PaletteSize * 5]; + Buffer.BlockCopy(buffer, 2, p.PaletteBuffer, 0, p.PaletteSize * 5); // save palette buffer in palette object + + return new PdsData + { + Message = "PalId: " + paletteId + ", update: " + paletteUpdate + ", " + p.PaletteSize + " entries", + PaletteId = paletteId, + PaletteVersion = paletteUpdate, + PaletteInfo = p, + }; + } + + /// + /// parse an ODS packet which contain the image data + /// + /// raw byte date, starting right after segment + /// object containing info about the current segment + /// true if this is a valid new object (neither invalid nor a fragment) + private static OdsData ParseOds(byte[] buffer, SupSegment segment, bool forceFirst) + { + int objId = BigEndianInt16(buffer, 0); // 16bit object_id + int objVer = buffer[2]; // 16bit object_id nikse - index 2 or 1??? + int objSeq = buffer[3]; // 8bit first_in_sequence (0x80), + // last_in_sequence (0x40), 6bits reserved + bool first = ((objSeq & 0x80) == 0x80) || forceFirst; + bool last = (objSeq & 0x40) == 0x40; + + var info = new ImageObjectFragment(); + if (first) + { + int width = BigEndianInt16(buffer, 7); // object_width + int height = BigEndianInt16(buffer, 9); // object_height + + info.ImagePacketSize = segment.Size - 11; // Image packet size (image bytes) + info.ImageBuffer = new byte[info.ImagePacketSize]; + Buffer.BlockCopy(buffer, 11, info.ImageBuffer, 0, info.ImagePacketSize); + + return new OdsData + { + IsFirst = true, + Size = new Size(width, height), + ObjectId = objId, + ObjectVersion = objVer, + Fragment = info, + Message = "ObjId: " + objId + ", ver: " + objVer + ", seq: first" + (last ? "/" : "") + (last ? "" + "last" : "") + ", width: " + width + ", height: " + height, + }; + } + else + { + info.ImagePacketSize = segment.Size - 4; + info.ImageBuffer = new byte[info.ImagePacketSize]; + Buffer.BlockCopy(buffer, 4, info.ImageBuffer, 0, info.ImagePacketSize); + + return new OdsData + { + IsFirst = false, + ObjectId = objId, + ObjectVersion = objVer, + Fragment = info, + Message = "Continued ObjId: " + objId + ", ver: " + objVer + ", seq: " + (last ? "" + "last" : ""), + }; + } + } + + public static List ParseBluRaySup(Stream ms, StringBuilder log, bool fromMatroskaFile) + { + long position = ms.Position; + int segmentCount = 0; + var palettes = new Dictionary>(); + bool forceFirstOds = true; + var bitmapObjects = new Dictionary>(); + PcsData latestPcs = null; + int latestCompNum = -1; + var pcsList = new List(); + byte[] headerBuffer; + if (fromMatroskaFile) + headerBuffer = new byte[3]; + else + headerBuffer = new byte[HeaderSize]; + + while (position < ms.Length) + { + ms.Seek(position, SeekOrigin.Begin); + + // Read segment header + ms.Read(headerBuffer, 0, headerBuffer.Length); + SupSegment segment; + if (fromMatroskaFile) + segment = ParseSegmentHeaderFromMatroska(headerBuffer); + else + segment = ParseSegmentHeader(headerBuffer, log); + position += headerBuffer.Length; + + // Read segment data + var buffer = new byte[segment.Size]; + ms.Read(buffer, 0, buffer.Length); + log.Append(segmentCount + ": "); + + switch (segment.Type) + { + case 0x14: // Palette + if (latestPcs != null) + { + log.AppendLine(string.Format("0x14 - Palette - PDS offset={0} size={1}", position, segment.Size)); + PdsData pds = ParsePds(buffer, segment); + log.AppendLine(pds.Message); + if (pds.PaletteInfo != null) + { + if (!palettes.ContainsKey(pds.PaletteId)) + { + palettes[pds.PaletteId] = new List(); + } + else + { + if (latestPcs.PaletteUpdate) + { + palettes[pds.PaletteId].RemoveAt(palettes[pds.PaletteId].Count - 1); + } + else + { + log.AppendLine("Extra Palette"); + } + } + palettes[pds.PaletteId].Add(pds.PaletteInfo); + } + } + break; + + case 0x15: // Image bitmap data + if (latestPcs != null) + { + log.AppendLine(string.Format("0x15 - Bitmap data - ODS offset={0} size={1}", position, segment.Size)); + OdsData ods = ParseOds(buffer, segment, forceFirstOds); + log.AppendLine(ods.Message); + if (!latestPcs.PaletteUpdate) + { + List odsList; + if (ods.IsFirst) + { + odsList = new List(); + odsList.Add(ods); + bitmapObjects[ods.ObjectId] = odsList; + } + else + { + if (bitmapObjects.TryGetValue(ods.ObjectId, out odsList)) + { + odsList.Add(ods); + } + else + { + log.AppendLine(string.Format("INVALID ObjectId {0} in ODS, offset={1}", ods.ObjectId, position)); + } + } + } + else + { + log.AppendLine(string.Format("Bitmap Data Ignore due to PaletteUpdate offset={0}", position)); + } + forceFirstOds = false; + } + break; + + case 0x16: // Picture time codes + if (latestPcs != null) + { + if (CompletePcs(latestPcs, bitmapObjects, palettes)) + { + pcsList.Add(latestPcs); + } + latestPcs = null; + } + + log.AppendLine(string.Format("0x16 - Picture codes, offset={0} size={1}", position, segment.Size)); + forceFirstOds = true; + PcsData nextPcs = ParsePicture(buffer, segment); + log.AppendLine(nextPcs.Message); + latestPcs = nextPcs; + latestCompNum = nextPcs.CompNum; + if (latestPcs.CompositionState == CompositionState.EpochStart) + { + bitmapObjects.Clear(); + palettes.Clear(); + } + break; + + case 0x17: // Window display + if (latestPcs != null) + { + log.AppendLine(string.Format("0x17 - Window display offset={0} size={1}", position, segment.Size)); + int windowCount = buffer[0]; + int offset = 0; + for (int nextWindow = 0; nextWindow < windowCount; nextWindow++) + { + int windowId = buffer[1 + offset]; + int x = BigEndianInt16(buffer, 2 + offset); + int y = BigEndianInt16(buffer, 4 + offset); + int width = BigEndianInt16(buffer, 6 + offset); + int height = BigEndianInt16(buffer, 8 + offset); + log.AppendLine(string.Format("WinId: {4}, X: {0}, Y: {1}, Width: {2}, Height: {3}", + x, y, width, height, windowId)); + offset += 9; + } + } + break; + + case 0x80: + forceFirstOds = true; + log.AppendLine(string.Format("0x80 - END offset={0} size={1}", position, segment.Size)); + if (latestPcs != null) + { + if (CompletePcs(latestPcs, bitmapObjects, palettes)) + { + pcsList.Add(latestPcs); + } + latestPcs = null; + } + break; + + default: + log.AppendLine(string.Format("0x?? - END offset={0} UNKOWN SEGMENT TYPE={1}", position, segment.Type)); + break; + } + position += segment.Size; + segmentCount++; + } + + if (latestPcs != null) + { + if (CompletePcs(latestPcs, bitmapObjects, palettes)) + pcsList.Add(latestPcs); + latestPcs = null; + } + + for (int pcsIndex = 1; pcsIndex < pcsList.Count; pcsIndex++) + pcsList[pcsIndex - 1].EndTime = pcsList[pcsIndex].StartTime; + pcsList.RemoveAll(pcs => pcs.PcsObjects.Count == 0); + + foreach (PcsData pcs in pcsList) + { + foreach (List odsList in pcs.BitmapObjects) + { + if (odsList.Count > 1) + { + int bufSize = 0; + foreach (OdsData ods in odsList) + bufSize += ods.Fragment.ImagePacketSize; + byte[] buf = new byte[bufSize]; + int offset = 0; + foreach (OdsData ods in odsList) + { + Buffer.BlockCopy(ods.Fragment.ImageBuffer, 0, buf, offset, ods.Fragment.ImagePacketSize); + offset += ods.Fragment.ImagePacketSize; + } + odsList[0].Fragment.ImageBuffer = buf; + odsList[0].Fragment.ImagePacketSize = bufSize; + while (odsList.Count > 1) + odsList.RemoveAt(1); + } + } + } + + for (int pcsIndex = pcsList.Count - 1; pcsIndex > 0; pcsIndex--) + { + var cur = pcsList[pcsIndex]; + var prev = pcsList[pcsIndex - 1]; + if (Math.Abs(prev.EndTime - cur.StartTime) < 10 && prev.Size.Width == cur.Size.Width && prev.Size.Height == cur.Size.Height) + { + if (cur.BitmapObjects.Count > 0 && cur.BitmapObjects[0].Count > 0 && + prev.BitmapObjects.Count > 0 && prev.BitmapObjects[0].Count > 0 && + ByteArraysEqual(cur.BitmapObjects[0][0].Fragment.ImageBuffer, prev.BitmapObjects[0][0].Fragment.ImageBuffer)) + { + prev.EndTime = cur.EndTime; + pcsList.RemoveAt(pcsIndex); + } + } + } + + return pcsList; + } + + private static bool ByteArraysEqual(byte[] b1, byte[] b2) + { + if (b1 == b2) + return true; + if (b1 == null || b2 == null) + return false; + if (b1.Length != b2.Length) + return false; + + for (int i = 0; i < b1.Length; i++) + { + if (b1[i] != b2[i]) + return false; + } + return true; + } + + private static CompositionState GetCompositionState(byte type) + { + switch (type) + { + case 0x00: + return CompositionState.Normal; + case 0x40: + return CompositionState.AcquPoint; + case 0x80: + return CompositionState.EpochStart; + case 0xC0: + return CompositionState.EpochContinue; + default: + return CompositionState.Invalid; + } + } + + private static int BigEndianInt16(byte[] buffer, int index) + { + if (buffer.Length < 2) + return 0; + return (buffer[index + 1]) + (buffer[index + 0] << 8); + } + + private static uint BigEndianInt32(byte[] buffer, int index) + { + if (buffer.Length < 4) + return 0; + return (uint)((buffer[index + 3]) + (buffer[index + 2] << 8) + (buffer[index + 1] << 0x10) + (buffer[index + 0] << 0x18)); + } + + } +} \ No newline at end of file diff --git a/libse/BluRaySup/BluRaySupPicture.cs b/libse/BluRaySup/BluRaySupPicture.cs new file mode 100644 index 000000000..403540d86 --- /dev/null +++ b/libse/BluRaySup/BluRaySupPicture.cs @@ -0,0 +1,572 @@ +/* + * Copyright 2009 Volker Oth (0xdeadbeef) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * NOTE: Converted to C# and modified by Nikse.dk@gmail.com + */ + +using System; +using System.Collections.Generic; +using System.Drawing; + +namespace Nikse.SubtitleEdit.Core.BluRaySup +{ + + public class BluRaySupPicture + { + /// + /// screen width + /// + public int Width { get; set; } + + /// + /// screen height + /// + public int Height { get; set; } + + /// + /// start time in milliseconds + /// + public long StartTime { get; set; } + + public int StartTimeForWrite + { + get { return (int)((StartTime - 45) * 90.0); } + } + + /// + /// end time in milliseconds + /// + public long EndTime { get; set; } + + public int EndTimeForWrite + { + get { return (int)((EndTime - 45) * 90.0); } + } + + /// + /// if true, this is a forced subtitle + /// + public bool IsForced { get; set; } + + /// + /// composition number - increased at start and end PCS + /// + public int CompositionNumber { get; set; } + + /// + /// objectID used in decoded object + /// + public int ObjectId { get; set; } + + /// + /// list of ODS packets containing image info + /// + public List ImageObjects; + + /// + /// width of subtitle window (might be larger than image) + /// + public int WindowWidth { get; set; } + + /// + /// height of subtitle window (might be larger than image) + /// + public int WindowHeight { get; set; } + + /// + /// upper left corner of subtitle window x + /// + public int WindowXOffset { get; set; } + + /// + /// upper left corner of subtitle window y + /// + public int WindowYOffset { get; set; } + + /// + /// FPS type (e.g. 0x10 = 24p) + /// + public int FramesPerSecondType { get; set; } + + /// + /// List of (list of) palette info - there are up to 8 palettes per epoch, each can be updated several times + /// + public List> Palettes; + + /// + /// Create RLE buffer from bitmap + /// + /// Bitmap to compress + /// Palette used for bitmap encoding + /// RLE buffer + private static byte[] EncodeImage(NikseBitmap bm, Dictionary palette) + { + var bytes = new List(); + for (int y = 0; y < bm.Height; y++) + { + var ofs = y * bm.Width; + //eol = false; + int x; + int len; + for (x = 0; x < bm.Width; x += len, ofs += len) + { + Color c = bm.GetPixel(x, y); + byte color; + if (palette.ContainsKey(c)) + color = (byte)palette[c]; + else + color = FindBestMatch(c, palette); + + for (len = 1; x + len < bm.Width; len++) + if (bm.GetPixel(x + len, y) != c) + break; + + if (len <= 2 && color != 0) + { + // only a single occurrence -> add color + bytes.Add(color); + if (len == 2) + bytes.Add(color); + } + else + { + if (len > 0x3fff) + len = 0x3fff; + bytes.Add(0); // rle id + // commented out due to bug in SupRip + /*if (color == 0 && x+len == bm.Width) + { + bytes.Add(0); + eol = true; + } + else */ + if (color == 0 && len < 0x40) + { + // 00 xx -> xx times 0 + bytes.Add((byte)len); + } + else if (color == 0) + { + // 00 4x xx -> xxx zeroes + bytes.Add((byte)(0x40 | (len >> 8))); + bytes.Add((byte)len); + } + else if (len < 0x40) + { + // 00 8x cc -> x times value cc + bytes.Add((byte)(0x80 | len)); + bytes.Add(color); + } + else + { + // 00 cx yy cc -> xyy times value cc + bytes.Add((byte)(0xc0 | (len >> 8))); + bytes.Add((byte)len); + bytes.Add(color); + } + } + } + if (/*!eol &&*/ x == bm.Width) + { + bytes.Add(0); // rle id + bytes.Add(0); + } + } + int size = bytes.Count; + var retval = new byte[size]; + for (int i = 0; i < size; i++) + retval[i] = bytes[i]; + return retval; + } + + private static byte FindBestMatch(Color color, Dictionary palette) + { + int smallestDiff = 1000; + int smallestDiffIndex = -1; + foreach (var kvp in palette) + { + int diff = Math.Abs(kvp.Key.A - color.A) + Math.Abs(kvp.Key.R - color.R) + Math.Abs(kvp.Key.G - color.G) + Math.Abs(kvp.Key.B - color.B); + if (diff < smallestDiff) + { + smallestDiff = diff; + smallestDiffIndex = kvp.Value; + } + } + return (byte)smallestDiffIndex; + } + + private static bool HasCloseColor(Color color, Dictionary palette, int maxDifference) + { + foreach (var kvp in palette) + { + int difference = Math.Abs(kvp.Key.A - color.A) + Math.Abs(kvp.Key.R - color.R) + Math.Abs(kvp.Key.G - color.G) + Math.Abs(kvp.Key.B - color.B); + if (difference < maxDifference) + return true; + } + return false; + } + + private static Dictionary GetBitmapPalette(NikseBitmap bitmap) + { + var pal = new Dictionary(); + for (int y = 0; y < bitmap.Height; y++) + { + for (int x = 0; x < bitmap.Width; x++) + { + var c = bitmap.GetPixel(x, y); + if (c != Color.Transparent) + { + if (pal.Count < 200 && !pal.ContainsKey(c)) + pal.Add(c, pal.Count); + else if (pal.Count < 254 && !HasCloseColor(c, pal, 5)) + pal.Add(c, pal.Count); + } + } + } + pal.Add(Color.Transparent, pal.Count); // last entry must be transparent + return pal; + } + + /// + /// Get ID for given frame rate + /// + /// frame rate + /// byte ID for the given frame rate + private static int GetFpsId(double fps) + { + if (Math.Abs(fps - Core.Fps24Hz) < 0.01) // 24 + return 0x20; + if (Math.Abs(fps - Core.FpsPal) < 0.01) // 25 + return 0x30; + if (Math.Abs(fps - Core.FpsNtsc) < 0.01) // 29.97 + return 0x40; + if (Math.Abs(fps - Core.FpsPalI) < 0.01) // 50 + return 0x60; + if (Math.Abs(fps - Core.FpsNtscI) < 0.1) // 59.94 + return 0x70; + + return 0x10; // 23.976 + } + + /// + /// Create the binary stream representation of one caption + /// + /// SubPicture object containing caption info + /// Bitmap + /// Frames per second + /// Image bottom margin + /// Image left/right margin + /// Alignment of image + /// Byte buffer containing the binary stream representation of one caption + public static byte[] CreateSupFrame(BluRaySupPicture pic, Bitmap bmp, double fps, int bottomMargin, int leftOrRightMargin, ContentAlignment alignment) + { + var bm = new NikseBitmap(bmp); + var colorPalette = GetBitmapPalette(bm); + var pal = new BluRaySupPalette(colorPalette.Count); + int k = 0; + foreach (var kvp in colorPalette) + { + pal.SetColor(k, kvp.Key); + k++; + } + + byte[] rleBuf = EncodeImage(bm, colorPalette); + + // for some obscure reason, a packet can be a maximum 0xfffc bytes + // since 13 bytes are needed for the header("PG", PTS, DTS, ID, SIZE) + // there are only 0xffef bytes available for the packet + // since the first ODS packet needs an additional 11 bytes for info + // and the following ODS packets need 4 additional bytes, the + // first package can store only 0xffe4 RLE buffer bytes and the + // following packets can store 0xffeb RLE buffer bytes + int numAddPackets; + if (rleBuf.Length <= 0xffe4) + numAddPackets = 0; // no additional packets needed; + else + numAddPackets = 1 + (rleBuf.Length - 0xffe4) / 0xffeb; + + // a typical frame consists of 8 packets. It can be enlonged by additional + // object frames + int palSize = colorPalette.Count; + + var packetHeader = new byte[] + { + 0x50, 0x47, // 0: "PG" + 0x00, 0x00, 0x00, 0x00, // 2: PTS - presentation time stamp + 0x00, 0x00, 0x00, 0x00, // 6: DTS - decoding time stamp + 0x00, // 10: segment_type + 0x00, 0x00 // 11: segment_length (bytes following till next PG) + }; + var headerPcsStart = new byte[] + { + 0x00, 0x00, 0x00, 0x00, // 0: video_width, video_height + 0x10, // 4: hi nibble: frame_rate (0x10=24p), lo nibble: reserved + 0x00, 0x00, // 5: composition_number (increased by start and end header) + 0x80, // 7: composition_state (0x80: epoch start) + 0x00, // 8: palette_update_flag (0x80), 7bit reserved + 0x00, // 9: palette_id_ref (0..7) + 0x01, // 10: number_of_composition_objects (0..2) + 0x00, 0x00, // 11: 16bit object_id_ref + 0x00, // 13: window_id_ref (0..1) + 0x00, // 14: object_cropped_flag: 0x80, forced_on_flag = 0x040, 6bit reserved + 0x00, 0x00, 0x00, 0x00 // 15: composition_object_horizontal_position, composition_object_vertical_position + }; + var headerPcsEnd = new byte[] + { + 0x00, 0x00, 0x00, 0x00, // 0: video_width, video_height + 0x10, // 4: hi nibble: frame_rate (0x10=24p), lo nibble: reserved + 0x00, 0x00, // 5: composition_number (increased by start and end header) + 0x00, // 7: composition_state (0x00: normal) + 0x00, // 8: palette_update_flag (0x80), 7bit reserved + 0x00, // 9: palette_id_ref (0..7) + 0x00 // 10: number_of_composition_objects (0..2) + }; + var headerWds = new byte[] + { + 0x01, // 0 : number of windows (currently assumed 1, 0..2 is legal) + 0x00, // 1 : window id (0..1) + 0x00, 0x00, 0x00, 0x00, // 2 : x-ofs, y-ofs + 0x00, 0x00, 0x00, 0x00 // 6 : width, height + }; + var headerOdsFirst = new byte[] + { + 0x00, 0x00, // 0: object_id + 0x00, // 2: object_version_number + 0xC0, // 3: first_in_sequence (0x80), last_in_sequence (0x40), 6bits reserved + 0x00, 0x00, 0x00, // 4: object_data_length - full RLE buffer length (including 4 bytes size info) + 0x00, 0x00, 0x00, 0x00 // 7: object_width, object_height + }; + var headerOdsNext = new byte[] + { + 0x00, 0x00, // 0: object_id + 0x00, // 2: object_version_number + 0x40 // 3: first_in_sequence (0x80), last_in_sequence (0x40), 6bits reserved + }; + + int size = packetHeader.Length * (8 + numAddPackets); + size += headerPcsStart.Length + headerPcsEnd.Length; + size += 2 * headerWds.Length + headerOdsFirst.Length; + size += numAddPackets * headerOdsNext.Length; + size += (2 + palSize * 5) /* PDS */; + size += rleBuf.Length; + + switch (alignment) + { + case ContentAlignment.BottomLeft: + pic.WindowXOffset = leftOrRightMargin; + pic.WindowYOffset = pic.Height - (bm.Height + bottomMargin); + break; + case ContentAlignment.BottomRight: + pic.WindowXOffset = pic.Width - bm.Width - bottomMargin; + pic.WindowYOffset = pic.Height - (bm.Height + leftOrRightMargin); + break; + case ContentAlignment.MiddleCenter: + pic.WindowXOffset = (pic.Width - bm.Width) / 2; + pic.WindowYOffset = (pic.Height - bm.Height) / 2; + break; + case ContentAlignment.MiddleLeft: + pic.WindowXOffset = leftOrRightMargin; + pic.WindowYOffset = (pic.Height - bm.Height) / 2; + break; + case ContentAlignment.MiddleRight: + pic.WindowXOffset = pic.Width - bm.Width - leftOrRightMargin; + pic.WindowYOffset = (pic.Height - bm.Height) / 2; + break; + case ContentAlignment.TopCenter: + pic.WindowXOffset = (pic.Width - bm.Width) / 2; + pic.WindowYOffset = bottomMargin; + break; + case ContentAlignment.TopLeft: + pic.WindowXOffset = leftOrRightMargin; + pic.WindowYOffset = bottomMargin; + break; + case ContentAlignment.TopRight: + pic.WindowXOffset = pic.Width - bm.Width - leftOrRightMargin; + pic.WindowYOffset = bottomMargin; + break; + default: // ContentAlignment.BottomCenter: + pic.WindowXOffset = (pic.Width - bm.Width) / 2; + pic.WindowYOffset = pic.Height - (bm.Height + bottomMargin); + break; + } + + int yOfs = pic.WindowYOffset - Core.CropOfsY; + if (yOfs < 0) + { + yOfs = 0; + } + else + { + int yMax = pic.Height - pic.WindowHeight - 2 * Core.CropOfsY; + if (yOfs > yMax) + yOfs = yMax; + } + + int h = pic.Height - 2 * Core.CropOfsY; + + var buf = new byte[size]; + int index = 0; + + int fpsId = GetFpsId(fps); + + /* time (in 90kHz resolution) needed to initialize (clear) the screen buffer + based on the composition pixel rate of 256e6 bit/s - always rounded up */ + int frameInitTime = (pic.Width * pic.Height * 9 + 3199) / 3200; // better use default height here + /* time (in 90kHz resolution) needed to initialize (clear) the window area + based on the composition pixel rate of 256e6 bit/s - always rounded up + Note: no cropping etc. -> window size == image size */ + int windowInitTime = (bm.Width * bm.Height * 9 + 3199) / 3200; + /* time (in 90kHz resolution) needed to decode the image + based on the decoding pixel rate of 128e6 bit/s - always rounded up */ + int imageDecodeTime = (bm.Width * bm.Height * 9 + 1599) / 1600; + // write PCS start + packetHeader[10] = 0x16; // ID + int dts = pic.StartTimeForWrite - (frameInitTime + windowInitTime + imageDecodeTime); //int dts = pic.StartTimeForWrite - windowInitTime; ??? + + ToolBox.SetDWord(packetHeader, 2, pic.StartTimeForWrite); // PTS + ToolBox.SetDWord(packetHeader, 6, dts); // DTS + ToolBox.SetWord(packetHeader, 11, headerPcsStart.Length); // size + for (int i = 0; i < packetHeader.Length; i++) + buf[index++] = packetHeader[i]; + ToolBox.SetWord(headerPcsStart, 0, pic.Width); + ToolBox.SetWord(headerPcsStart, 2, h); // cropped height + ToolBox.SetByte(headerPcsStart, 4, fpsId); + ToolBox.SetWord(headerPcsStart, 5, pic.CompositionNumber); + headerPcsStart[14] = (byte)(pic.IsForced ? 0x40 : 0); + ToolBox.SetWord(headerPcsStart, 15, pic.WindowXOffset); + ToolBox.SetWord(headerPcsStart, 17, yOfs); + for (int i = 0; i < headerPcsStart.Length; i++) + buf[index++] = headerPcsStart[i]; + + // write WDS + packetHeader[10] = 0x17; // ID + int timestamp = pic.StartTimeForWrite - windowInitTime; + ToolBox.SetDWord(packetHeader, 2, timestamp); // PTS (keep DTS) + ToolBox.SetWord(packetHeader, 11, headerWds.Length); // size + for (int i = 0; i < packetHeader.Length; i++) + buf[index++] = packetHeader[i]; + ToolBox.SetWord(headerWds, 2, pic.WindowXOffset); + ToolBox.SetWord(headerWds, 4, yOfs); + ToolBox.SetWord(headerWds, 6, bm.Width); + ToolBox.SetWord(headerWds, 8, bm.Height); + for (int i = 0; i < headerWds.Length; i++) + buf[index++] = headerWds[i]; + + // write PDS + packetHeader[10] = 0x14; // ID + ToolBox.SetDWord(packetHeader, 2, dts); // PTS (=DTS of PCS/WDS) + ToolBox.SetDWord(packetHeader, 6, 0); // DTS (0) + ToolBox.SetWord(packetHeader, 11, (2 + palSize * 5)); // size + for (int i = 0; i < packetHeader.Length; i++) + buf[index++] = packetHeader[i]; + buf[index++] = 0; + buf[index++] = 0; + for (int i = 0; i < palSize; i++) + { + buf[index++] = (byte)i; // index + buf[index++] = pal.GetY()[i]; // Y + buf[index++] = pal.GetCr()[i]; // Cr + buf[index++] = pal.GetCb()[i]; // Cb + buf[index++] = pal.GetAlpha()[i]; // Alpha + } + + // write first OBJ + int bufSize = rleBuf.Length; + int rleIndex = 0; + if (bufSize > 0xffe4) + bufSize = 0xffe4; + packetHeader[10] = 0x15; // ID + timestamp = dts + imageDecodeTime; + ToolBox.SetDWord(packetHeader, 2, timestamp); // PTS + ToolBox.SetDWord(packetHeader, 6, dts); // DTS + ToolBox.SetWord(packetHeader, 11, headerOdsFirst.Length + bufSize); // size + for (int i = 0; i < packetHeader.Length; i++) + buf[index++] = packetHeader[i]; + int marker = (int)((numAddPackets == 0) ? 0xC0000000 : 0x80000000); + ToolBox.SetDWord(headerOdsFirst, 3, marker | (rleBuf.Length + 4)); + ToolBox.SetWord(headerOdsFirst, 7, bm.Width); + ToolBox.SetWord(headerOdsFirst, 9, bm.Height); + for (int i = 0; i < headerOdsFirst.Length; i++) + buf[index++] = headerOdsFirst[i]; + for (int i = 0; i < bufSize; i++) + buf[index++] = rleBuf[rleIndex++]; + + // write additional OBJ packets + bufSize = rleBuf.Length - bufSize; // remaining bytes to write + for (int p = 0; p < numAddPackets; p++) + { + int psize = bufSize; + if (psize > 0xffeb) + psize = 0xffeb; + packetHeader[10] = 0x15; // ID (keep DTS & PTS) + ToolBox.SetWord(packetHeader, 11, headerOdsNext.Length + psize); // size + for (int i = 0; i < packetHeader.Length; i++) + buf[index++] = packetHeader[i]; + for (int i = 0; i < headerOdsNext.Length; i++) + buf[index++] = headerOdsNext[i]; + for (int i = 0; i < psize; i++) + buf[index++] = rleBuf[rleIndex++]; + bufSize -= psize; + } + + // write END + packetHeader[10] = 0x80; // ID + ToolBox.SetDWord(packetHeader, 6, 0); // DTS (0) (keep PTS of ODS) + ToolBox.SetWord(packetHeader, 11, 0); // size + for (int i = 0; i < packetHeader.Length; i++) + buf[index++] = packetHeader[i]; + + // write PCS end + packetHeader[10] = 0x16; // ID + ToolBox.SetDWord(packetHeader, 2, pic.EndTimeForWrite); // PTS + dts = pic.EndTimeForWrite - 1; //dts = pic.StartTimeForWrite - 1; + ToolBox.SetDWord(packetHeader, 6, dts); // DTS + ToolBox.SetWord(packetHeader, 11, headerPcsEnd.Length); // size + for (int i = 0; i < packetHeader.Length; i++) + buf[index++] = packetHeader[i]; + ToolBox.SetWord(headerPcsEnd, 0, pic.Width); + ToolBox.SetWord(headerPcsEnd, 2, h); // cropped height + ToolBox.SetByte(headerPcsEnd, 4, fpsId); + ToolBox.SetWord(headerPcsEnd, 5, pic.CompositionNumber + 1); + for (int i = 0; i < headerPcsEnd.Length; i++) + buf[index++] = headerPcsEnd[i]; + + // write WDS + packetHeader[10] = 0x17; // ID + timestamp = pic.EndTimeForWrite - windowInitTime; + ToolBox.SetDWord(packetHeader, 2, timestamp); // PTS (keep DTS of PCS) + ToolBox.SetWord(packetHeader, 11, headerWds.Length); // size + for (int i = 0; i < packetHeader.Length; i++) + buf[index++] = packetHeader[i]; + ToolBox.SetWord(headerWds, 2, pic.WindowXOffset); + ToolBox.SetWord(headerWds, 4, yOfs); + ToolBox.SetWord(headerWds, 6, bm.Width); + ToolBox.SetWord(headerWds, 8, bm.Height); + for (int i = 0; i < headerWds.Length; i++) + buf[index++] = headerWds[i]; + + // write END + packetHeader[10] = 0x80; // ID + ToolBox.SetDWord(packetHeader, 2, dts); // PTS (DTS of end PCS) + ToolBox.SetDWord(packetHeader, 6, 0); // DTS (0) + ToolBox.SetWord(packetHeader, 11, 0); // size + for (int i = 0; i < packetHeader.Length; i++) + buf[index++] = packetHeader[i]; + + return buf; + } + + } +} \ No newline at end of file diff --git a/libse/BluRaySup/Core.cs b/libse/BluRaySup/Core.cs new file mode 100644 index 000000000..c31f681fa --- /dev/null +++ b/libse/BluRaySup/Core.cs @@ -0,0 +1,90 @@ +/* + * Copyright 2009 Volker Oth (0xdeadbeef) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * NOTE: Converted to C# and modified by Nikse.dk@gmail.com + */ +namespace Nikse.SubtitleEdit.Core.BluRaySup +{ + public static class Core + { + + /** Use BT.601 color model instead of BT.709 */ + private const bool UseBt601 = false; + + /** Flag that defines whether to swap Cr/Cb components when loading a SUP */ + private const bool SwapCrCb = false; + + /** Alpha threshold for cropping */ + private const int AlphaCrop = 14; + + /** Two equal captions are merged of they are closer than 200ms (0.2*90000 = 18000) */ + private const int MergePtSdiff = 18000; + + /** Frames per seconds for 24p (23.976) */ + public const double Fps24P = 24000.0 / 1001; + /** Frames per seconds for wrong 24P (23.975) */ + public const double Fps23975 = 23.975; + /** Frames per seconds for 24Hz (24.0) */ + public const double Fps24Hz = 24.0; + /** Frames per seconds for PAL progressive (25.0) */ + public const double FpsPal = 25.0; + /** Frames per seconds for NTSC progressive (29.97) */ + public const double FpsNtsc = 30000.0 / 1001; + /** Frames per seconds for PAL interlaced (50.0) */ + public const double FpsPalI = 50.0; + /** Frames per seconds for NTSC interlaced (59.94) */ + public const double FpsNtscI = 60000.0 / 1001; + + /** + * Get maximum time difference for merging captions. + * @return Maximum time difference for merging captions + */ + public static int GetMergePtSdiff() + { + return MergePtSdiff; + } + + /** + * Get: use of BT.601 color model instead of BT.709. + * @return True if BT.601 is used + */ + public static bool UsesBt601() + { + return UseBt601; + } + + /** + * Get flag that defines whether to swap Cr/Cb components when loading a SUP. + * @return True: swap cr/cb + */ + public static bool GetSwapCrCb() + { + return SwapCrCb; + } + + /** + * Get alpha threshold for cropping. + * @return Alpha threshold for cropping + */ + public static int GetAlphaCrop() + { + return AlphaCrop; + } + + public static int CropOfsY { get; set; } + + public const double FpsTrg = FpsPal; + } +} \ No newline at end of file diff --git a/libse/BluRaySup/ImageObject.cs b/libse/BluRaySup/ImageObject.cs new file mode 100644 index 000000000..f03225e26 --- /dev/null +++ b/libse/BluRaySup/ImageObject.cs @@ -0,0 +1,60 @@ +/* + * Copyright 2009 Volker Oth (0xdeadbeef) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * NOTE: Converted to C# and modified by Nikse.dk@gmail.com + */ + +using System.Collections.Generic; + +namespace Nikse.SubtitleEdit.Core.BluRaySup +{ + public class ImageObject + { + /// + /// list of ODS packets containing image info + /// + public List Fragments; + + /// + /// palette identifier + /// + public int PaletteId { get; set; } + + /// + /// overall size of RLE buffer (might be spread over several packages) + /// + public int BufferSize { get; set; } + + /// + /// with of subtitle image + /// + public int Width { get; set; } + + /// + /// height of subtitle image + /// + public int Height { get; set; } + + /// + /// upper left corner of subtitle x + /// + public int XOffset { get; set; } + + /// + /// upper left corner of subtitle y + /// + public int YOffset { get; set; } + } +} \ No newline at end of file diff --git a/libse/BluRaySup/ImageObjectFragment.cs b/libse/BluRaySup/ImageObjectFragment.cs new file mode 100644 index 000000000..a3cc65053 --- /dev/null +++ b/libse/BluRaySup/ImageObjectFragment.cs @@ -0,0 +1,37 @@ +/* + * Copyright 2009 Volker Oth (0xdeadbeef) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * NOTE: Converted to C# and modified by Nikse.dk@gmail.com + */ + +namespace Nikse.SubtitleEdit.Core.BluRaySup +{ + /// + /// contains offset and size of one fragment containing (parts of the) RLE buffer + /// + public class ImageObjectFragment + { + /// + /// size of this part of the RLE buffer + /// + public int ImagePacketSize { get; set; } + + /// + /// Buffer for raw image data fragment + /// + public byte[] ImageBuffer { get; set; } + } + +} \ No newline at end of file diff --git a/libse/BluRaySup/PaletteInfo.cs b/libse/BluRaySup/PaletteInfo.cs new file mode 100644 index 000000000..6ea146080 --- /dev/null +++ b/libse/BluRaySup/PaletteInfo.cs @@ -0,0 +1,50 @@ +/* + * Copyright 2009 Volker Oth (0xdeadbeef) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * NOTE: Converted to C# and modified by Nikse.dk@gmail.com + */ + +using System; + +namespace Nikse.SubtitleEdit.Core.BluRaySup +{ + /// + /// contains offset and size of one update of a palette + /// + public class PaletteInfo + { + /// + /// number of palette entries + /// + public int PaletteSize { get; set; } + + /// + /// raw palette data + /// + public byte[] PaletteBuffer { get; set; } + + public PaletteInfo() + { + } + + public PaletteInfo(PaletteInfo paletteInfo) + { + PaletteSize = paletteInfo.PaletteSize; + PaletteBuffer = new byte[paletteInfo.PaletteBuffer.Length]; + Buffer.BlockCopy(paletteInfo.PaletteBuffer, 0, PaletteBuffer, 0, PaletteBuffer.Length); + } + + } +} \ No newline at end of file diff --git a/libse/BluRaySup/ToolBox.cs b/libse/BluRaySup/ToolBox.cs new file mode 100644 index 000000000..5d6fcb1de --- /dev/null +++ b/libse/BluRaySup/ToolBox.cs @@ -0,0 +1,122 @@ +/* + * Copyright 2009 Volker Oth (0xdeadbeef) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * NOTE: Converted to C# and modified by Nikse.dk@gmail.com + */ + +using System.Text; + +namespace Nikse.SubtitleEdit.Core.BluRaySup +{ + public static class ToolBox + { + /// + /// Convert bytes to a C-style hex string with leading zeroes + /// + public static string ToHex(byte[] buffer, int index, int digits) + { + var sb = new StringBuilder(); + for (int i = index; i < index + digits; i++) + { + string s = string.Format("{0:X}", buffer[i]); + if (s.Length < 2) + sb.Append('0'); + sb.Append(s); + } + return "0x" + sb; + } + + /// + /// Convert a long integer to a C-style hex string with leading zeroes + /// + public static string ToHex(int number, int digits) + { + string s = string.Format("{0:X}", number); + if (s.Length < digits) + s = s.PadLeft(digits, '0'); + return "0x" + s; + } + + /** + * Convert time in milliseconds to array containing hours, minutes, seconds and milliseconds + * @param ms Time in milliseconds + * @return Array containing hours, minutes, seconds and milliseconds (in this order) + */ + public static int[] MillisecondsToTime(long ms) + { + int[] time = new int[4]; + // time[0] = hours + time[0] = (int)(ms / (60 * 60 * 1000)); + ms -= time[0] * 60 * 60 * 1000; + // time[1] = minutes + time[1] = (int)(ms / (60 * 1000)); + ms -= time[1] * 60 * 1000; + // time[2] = seconds + time[2] = (int)(ms / 1000); + ms -= time[2] * 1000; + time[3] = (int)ms; + return time; + } + + /// + /// Convert time in 90kHz ticks to string hh:mm:ss.ms + /// + /// Time in 90kHz resolution + /// String in format hh:mm:ss:ms + public static string PtsToTimeString(long pts) + { + int[] time = MillisecondsToTime((pts + 45) / 90); + return string.Format(@"{0:D2}:{1:D2}:{2:D2}.{3:D3}", time[0], time[1], time[2], time[3]); + } + + /// + /// Write (big endian) double word to buffer[index] (index points at most significant byte) + /// + /// Byte array + /// Index to write to + /// Integer value of double word to write + public static void SetDWord(byte[] buffer, int index, int val) + { + buffer[index] = (byte)(val >> 24); + buffer[index + 1] = (byte)(val >> 16); + buffer[index + 2] = (byte)(val >> 8); + buffer[index + 3] = (byte)(val); + } + + /// + /// Write (big endian) word to buffer[index] (index points at most significant byte) + /// + /// Byte array + /// index Index to write to + /// val Integer value of word to write + public static void SetWord(byte[] buffer, int index, int val) + { + buffer[index] = (byte)(val >> 8); + buffer[index + 1] = (byte)(val); + } + + /// + /// Write byte to buffer[index] + /// + /// Byte array + /// Index to write to + /// Integer value of byte to write + public static void SetByte(byte[] buffer, int index, int val) + { + buffer[index] = (byte)(val); + } + + } +} \ No newline at end of file diff --git a/libse/BmpReader.cs b/libse/BmpReader.cs new file mode 100644 index 000000000..388753931 --- /dev/null +++ b/libse/BmpReader.cs @@ -0,0 +1,22 @@ +using System; + +namespace Nikse.SubtitleEdit.Core +{ + public class BmpReader + { + + public string HeaderId { get; private set; } + public UInt32 HeaderFileSize { get; private set; } + public UInt32 OffsetToPixelArray { get; private set; } + + public BmpReader(string fileName) + { + byte[] buffer = System.IO.File.ReadAllBytes(fileName); + HeaderId = System.Text.Encoding.UTF8.GetString(buffer, 0, 2); + HeaderFileSize = BitConverter.ToUInt32(buffer, 2); + OffsetToPixelArray = BitConverter.ToUInt32(buffer, 0xa); + } + + } + +} diff --git a/libse/Configuration.cs b/libse/Configuration.cs new file mode 100644 index 000000000..ec3bcb05a --- /dev/null +++ b/libse/Configuration.cs @@ -0,0 +1,230 @@ +using System; +using System.IO; + +namespace Nikse.SubtitleEdit.Core +{ + /// + /// Configuration settings via Singleton pattern + /// + public class Configuration + { + private static readonly Lazy _instance = new Lazy(() => new Configuration()); + + private readonly string _baseDir; + private readonly string _dataDir; + private readonly Lazy _settings; + + private Configuration() + { + _baseDir = GetBaseDirectory(); + _dataDir = GetDataDirectory(); + _settings = new Lazy(Settings.GetSettings); + } + + public static string SettingsFileName + { + get + { + return DataDirectory + "Settings.xml"; + } + } + + public static string DictionariesFolder + { + get + { + return DataDirectory + "Dictionaries" + Path.DirectorySeparatorChar; + } + } + + public static string IconsFolder + { + get + { + return BaseDirectory + "Icons" + Path.DirectorySeparatorChar; + } + } + + public static bool IsRunningOnLinux() + { + return Environment.OSVersion.Platform == PlatformID.Unix; + } + + public static bool IsRunningOnMac() + { + return Environment.OSVersion.Platform == PlatformID.MacOSX; + } + + public static string TesseractDataFolder + { + get + { + if (IsRunningOnLinux() || IsRunningOnMac()) + { + if (Directory.Exists("/usr/share/tesseract-ocr/tessdata")) + return "/usr/share/tesseract-ocr/tessdata"; + if (Directory.Exists("/usr/share/tesseract/tessdata")) + return "/usr/share/tesseract/tessdata"; + if (Directory.Exists("/usr/share/tessdata")) + return "/usr/share/tessdata"; + } + return TesseractFolder + "tessdata"; + } + } + + public static string TesseractOriginalFolder + { + get + { + return BaseDirectory + "Tesseract" + Path.DirectorySeparatorChar; + } + } + + public static string TesseractFolder + { + get + { + return DataDirectory + "Tesseract" + Path.DirectorySeparatorChar; + } + } + + public static string VobSubCompareFolder + { + get + { + return DataDirectory + "VobSub" + Path.DirectorySeparatorChar; + } + } + + public static string OcrFolder + { + get + { + return DataDirectory + "Ocr" + Path.DirectorySeparatorChar; + } + } + + public static string WaveformsFolder + { + get + { + return DataDirectory + "Waveforms" + Path.DirectorySeparatorChar; + } + } + + public static string SpectrogramsFolder + { + get + { + return DataDirectory + "Spectrograms" + Path.DirectorySeparatorChar; + } + } + + public static string AutoBackupFolder + { + get + { + return DataDirectory + "AutoBackup" + Path.DirectorySeparatorChar; + } + } + + public static string PluginsDirectory + { + get + { + return Path.Combine(DataDirectory, "Plugins"); + } + } + + public static string DataDirectory + { + get + { + return _instance.Value._dataDir; + } + } + + public static string BaseDirectory + { + get + { + return _instance.Value._baseDir; + } + } + + public static Settings Settings + { + get + { + return _instance.Value._settings.Value; + } + } + + private static string GetInstallerPath() + { + const string valueName = "InstallLocation"; + var value = RegistryUtil.GetValue(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\SubtitleEdit_is1", valueName); + if (value != null && Directory.Exists(value)) + { + return value; + } + + value = RegistryUtil.GetValue(@"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\SubtitleEdit_is1", valueName); + if (value != null && Directory.Exists(value)) + { + return value; + } + + return null; + } + + private static string GetBaseDirectory() + { + var assembly = System.Reflection.Assembly.GetEntryAssembly(); + var baseDirectory = Path.GetDirectoryName(assembly == null + ? System.Reflection.Assembly.GetExecutingAssembly().Location + : assembly.Location); + + return baseDirectory.EndsWith(Path.DirectorySeparatorChar) + ? baseDirectory + : baseDirectory + Path.DirectorySeparatorChar; + } + + private string GetDataDirectory() + { + if (IsRunningOnLinux() || IsRunningOnMac()) + { + return _baseDir; + } + + var installerPath = GetInstallerPath(); + var hasUninstallFiles = Directory.GetFiles(_baseDir, "unins*.*").Length > 0; + var hasDictionaryFolder = Directory.Exists(Path.Combine(_baseDir, "Dictionaries")); + var appDataRoamingPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Subtitle Edit"); + + if ((installerPath == null || !installerPath.TrimEnd(Path.DirectorySeparatorChar).Equals(_baseDir.TrimEnd(Path.DirectorySeparatorChar), StringComparison.OrdinalIgnoreCase)) + && !hasUninstallFiles && (hasDictionaryFolder || !Directory.Exists(Path.Combine(appDataRoamingPath, "Dictionaries")))) + { + return _baseDir; + } + + if (Directory.Exists(appDataRoamingPath)) + { + return appDataRoamingPath + Path.DirectorySeparatorChar; + } + + try + { + Directory.CreateDirectory(appDataRoamingPath); + Directory.CreateDirectory(Path.Combine(appDataRoamingPath, "Dictionaries")); + return appDataRoamingPath + Path.DirectorySeparatorChar; + } + catch + { + System.Windows.Forms.MessageBox.Show("Please re-install Subtitle Edit (installer version)"); + System.Windows.Forms.Application.ExitThread(); + return _baseDir; + } + } + } +} \ No newline at end of file diff --git a/libse/ContainerFormats/AviRiffData.cs b/libse/ContainerFormats/AviRiffData.cs new file mode 100644 index 000000000..f1989959e --- /dev/null +++ b/libse/ContainerFormats/AviRiffData.cs @@ -0,0 +1,147 @@ +// (c) Giora Tamir (giora@gtamir.com), 2005 + +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.ContainerFormats +{ + [StructLayout(LayoutKind.Sequential, Pack = 1)] + internal struct AVIMAINHEADER + { // 'avih' + public int dwMicroSecPerFrame; + public int dwMaxBytesPerSec; + public int dwPaddingGranularity; + public int dwFlags; + public int dwTotalFrames; + public int dwInitialFrames; + public int dwStreams; + public int dwSuggestedBufferSize; + public int dwWidth; + public int dwHeight; + public int dwReserved0; + public int dwReserved1; + public int dwReserved2; + public int dwReserved3; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + internal struct AVIEXTHEADER + { // 'dmlh' + public int dwGrandFrames; // total number of frames in the file + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 244)] + public int[] dwFuture; // to be defined later + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + internal struct RECT + { + public short left; + public short top; + public short right; + public short bottom; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + internal struct AVISTREAMHEADER + { // 'strh' + public int fccType; // stream type codes + public int fccHandler; + public int dwFlags; + public short wPriority; + public short wLanguage; + public int dwInitialFrames; + public int dwScale; + public int dwRate; // dwRate/dwScale is stream tick rate in ticks/s + public int dwStart; + public int dwLength; + public int dwSuggestedBufferSize; + public int dwQuality; + public int dwSampleSize; + public RECT rcFrame; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + internal struct AVIOLDINDEXENTRY + { + public int dwChunkId; + public int dwFlags; + public int dwOffset; // offset of riff chunk header for the data + public int dwSize; // size of the data (excluding riff header size) + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + internal struct TIMECODE + { + public short wFrameRate; + public short wFrameFract; + public int cFrames; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + internal struct TIMECODEDATA + { + private TIMECODE time; + public int dwSMPTEflags; + public int dwUser; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + internal struct WAVEFORMATEX + { + public short wFormatTag; + public short nChannels; + public int nSamplesPerSec; + public int nAvgBytesPerSec; + public short nBlockAlign; + public short wBitsPerSample; + public short cbSize; + } + + internal static class AviRiffData + { + #region AVI constants + + // AVIMAINHEADER flags + public const int AVIF_HASINDEX = 0x00000010; // Index at end of file? + public const int AVIF_MUSTUSEINDEX = 0x00000020; + public const int AVIF_ISINTERLEAVED = 0x00000100; + public const int AVIF_TRUSTCKTYPE = 0x00000800; // Use CKType to find key frames + public const int AVIF_WASCAPTUREFILE = 0x00010000; + public const int AVIF_COPYRIGHTED = 0x00020000; + + // AVISTREAMINFO flags + public const int AVISF_DISABLED = 0x00000001; + public const int AVISF_VIDEO_PALCHANGES = 0x00010000; + + // AVIOLDINDEXENTRY flags + public const int AVIIF_LIST = 0x00000001; + public const int AVIIF_KEYFRAME = 0x00000010; + public const int AVIIF_NO_TIME = 0x00000100; + public const int AVIIF_COMPRESSOR = 0x0FFF0000; // unused? + + // TIMECODEDATA flags + public const int TIMECODE_SMPTE_BINARY_GROUP = 0x07; + public const int TIMECODE_SMPTE_COLOR_FRAME = 0x08; + + // AVI stream FourCC codes + public static readonly int streamtypeVIDEO = RiffParser.ToFourCC("vids"); + public static readonly int streamtypeAUDIO = RiffParser.ToFourCC("auds"); + //public static readonly int streamtypeMIDI = RiffParser.ToFourCC("mids"); + //public static readonly int streamtypeTEXT = RiffParser.ToFourCC("txts"); + + // AVI section FourCC codes + public static readonly int ckidAVIHeaderList = RiffParser.ToFourCC("hdrl"); + public static readonly int ckidMainAVIHeader = RiffParser.ToFourCC("avih"); + //public static readonly int ckidODML = RiffParser.ToFourCC("odml"); + //public static readonly int ckidAVIExtHeader = RiffParser.ToFourCC("dmlh"); + public static readonly int ckidAVIStreamList = RiffParser.ToFourCC("strl"); + public static readonly int ckidAVIStreamHeader = RiffParser.ToFourCC("strh"); + //public static readonly int ckidStreamFormat = RiffParser.ToFourCC("strf"); + //public static readonly int ckidAVIOldIndex = RiffParser.ToFourCC("idx1"); + public static readonly int ckidINFOList = RiffParser.ToFourCC("INFO"); + public static readonly int ckidAVIISFT = RiffParser.ToFourCC("ISFT"); + public const int ckidMP3 = 0x0055; + public static readonly int ckidWaveFMT = RiffParser.ToFourCC("fmt "); + + #endregion AVI constants + } +} diff --git a/libse/ContainerFormats/Ebml/Element.cs b/libse/ContainerFormats/Ebml/Element.cs new file mode 100644 index 000000000..2e3e51add --- /dev/null +++ b/libse/ContainerFormats/Ebml/Element.cs @@ -0,0 +1,53 @@ +namespace Nikse.SubtitleEdit.Core.ContainerFormats.Ebml +{ + internal class Element + { + private readonly ElementId _id; + private readonly long _dataPosition; + private readonly long _dataSize; + + public Element(ElementId id, long dataPosition, long dataSize) + { + _id = id; + _dataPosition = dataPosition; + _dataSize = dataSize; + } + + public ElementId Id + { + get + { + return _id; + } + } + + public long DataPosition + { + get + { + return _dataPosition; + } + } + + public long DataSize + { + get + { + return _dataSize; + } + } + + public long EndPosition + { + get + { + return _dataPosition + _dataSize; + } + } + + public override string ToString() + { + return string.Format(@"{0} ({1})", _id, _dataSize); + } + } +} \ No newline at end of file diff --git a/libse/ContainerFormats/Ebml/ElementId.cs b/libse/ContainerFormats/Ebml/ElementId.cs new file mode 100644 index 000000000..ca2816504 --- /dev/null +++ b/libse/ContainerFormats/Ebml/ElementId.cs @@ -0,0 +1,43 @@ +namespace Nikse.SubtitleEdit.Core.ContainerFormats.Ebml +{ + internal enum ElementId + { + None = 0, + + Ebml = 0x1A45DFA3, + Segment = 0x18538067, + + Info = 0x1549A966, + TimecodeScale = 0x2AD7B1, + Duration = 0x4489, + + Tracks = 0x1654AE6B, + TrackEntry = 0xAE, + TrackNumber = 0xD7, + TrackType = 0x83, + DefaultDuration = 0x23E383, + Name = 0x536E, + Language = 0x22B59C, + CodecId = 0x86, + CodecPrivate = 0x63A2, + Video = 0xE0, + PixelWidth = 0xB0, + PixelHeight = 0xBA, + Audio = 0xE1, + ContentEncodings = 0x6D80, + ContentEncoding = 0x6240, + ContentEncodingOrder = 0x5031, + ContentEncodingScope = 0x5032, + ContentEncodingType = 0x5033, + ContentCompression = 0x5034, + ContentCompAlgo = 0x4254, + ContentCompSettings = 0x4255, + + Cluster = 0x1F43B675, + Timecode = 0xE7, + SimpleBlock = 0xA3, + BlockGroup = 0xA0, + Block = 0xA1, + BlockDuration = 0x9B + } +} diff --git a/libse/ContainerFormats/MaterialExchangeFormat/KeyIdentifier.cs b/libse/ContainerFormats/MaterialExchangeFormat/KeyIdentifier.cs new file mode 100644 index 000000000..86409c819 --- /dev/null +++ b/libse/ContainerFormats/MaterialExchangeFormat/KeyIdentifier.cs @@ -0,0 +1,16 @@ +namespace Nikse.SubtitleEdit.Core.ContainerFormats.MaterialExchangeFormat +{ + public enum KeyIdentifier + { + Unknown, + PartitionPack, + Preface, + EssenceElement, + OperationalPattern, + PartitionMetadata, + StructuralMetadata, + DataDefinitionVideo, + DataDefinitionAudio, + Primer + } +} diff --git a/libse/ContainerFormats/MaterialExchangeFormat/KlvPacket.cs b/libse/ContainerFormats/MaterialExchangeFormat/KlvPacket.cs new file mode 100644 index 000000000..8f137770a --- /dev/null +++ b/libse/ContainerFormats/MaterialExchangeFormat/KlvPacket.cs @@ -0,0 +1,155 @@ +using System; +using System.IO; +using System.Text; +using Nikse.SubtitleEdit.Core.VobSub; + +namespace Nikse.SubtitleEdit.Core.ContainerFormats.MaterialExchangeFormat +{ + /// + /// Key-Length-Value MXF package - http://en.wikipedia.org/wiki/KLV + http://en.wikipedia.org/wiki/Material_Exchange_Format + /// + public class KlvPacket + { + public static byte[] PartitionPack = { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x05, 0x01, 0x01, 0x0D, 0x01, 0x02, 0x01, 0x01, 0xff, 0xff, 0x00 }; // 0xff can have different values + public static byte[] Preface = { 0x06, 0x0E, 0x2B, 0x34, 0x02, 0x53, 0x01, 0x01, 0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x2F, 0x00 }; + public static byte[] EssenceElement = { 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x02, 0x01, 0x01, 0x0D, 0x01, 0x03, 0x01, 0xff, 0xff, 0xff, 0xff }; + public static byte[] OperationalPattern = { 0x06, 0x0E, 0x2B, 0x34, 0x04, 0x01, 0x01, 0x01, 0x0D, 0x01, 0x02, 0x01, 0x00, 0xff, 0xff, 0x00 }; + public static byte[] PartitionMetadata = { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x05, 0x01, 0x01, 0x0d, 0x01, 0x02, 0x01, 0x01, 0x04, 0x04, 0x00 }; + public static byte[] StructuralMetadata = { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, 0x0d, 0x01, 0x01, 0x01, 0x00, 0xff, 0xff, 0x00 }; + public static byte[] DataDefinitionVideo = { 0x06, 0x0E, 0x2B, 0x34, 0x04, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02, 0x02, 0x01, 0xff, 0xff, 0x00 }; + public static byte[] DataDefinitionAudio = { 0x06, 0x0E, 0x2B, 0x34, 0x04, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02, 0x02, 0x02, 0xff, 0xff, 0x00 }; + public static byte[] Primer = { 0x06, 0xe, 0x2b, 0x34, 0x02, 0x05, 0x1, 0xff, 0x0d, 0x01, 0x02, 0x01, 0x01, 0x05, 0x01, 0xff }; + + private const int KeySize = 16; + + public byte[] Key; + public long DataSize; + public long TotalSize; + public long DataPosition; + public PartitionStatus PartionStatus = PartitionStatus.Unknown; + + public KlvPacket(Stream stream) + { + // read 16-bytes key + Key = new byte[KeySize]; + int read = stream.Read(Key, 0, Key.Length); + if (read < Key.Length) + { + throw new Exception("MXF KLV packet - stream does not have 16 bytes available for key"); + } + int lengthSize; + DataSize = GetBasicEncodingRuleLength(stream, out lengthSize); + DataPosition = stream.Position; + TotalSize = KeySize + lengthSize + DataSize; + if (Key[14] >= 1 && Key[14] <= 4) + { + PartionStatus = (PartitionStatus)Key[14]; + } + } + + /// + /// Read length - never be more than 9 bytes in size (which means max 8 bytes of payload length) + /// There are four kinds of encoding for the Length field: 1-byte, 2-byte, 4-byte + /// + /// + /// + /// + private long GetBasicEncodingRuleLength(Stream stream, out int bytesInLength) + { + int first = stream.ReadByte(); + if (first > 127) // first bit set + { + bytesInLength = first & Helper.B01111111; + if (bytesInLength > 8) + { + throw new Exception("MXF KLV packet - lenght bytes > 8"); + } + DataSize = 0; + for (int i = 0; i < bytesInLength; i++) + { + DataSize = DataSize * 256 + stream.ReadByte(); + } + bytesInLength++; + return DataSize; + } + bytesInLength = 1; + return first; + } + + public KeyIdentifier IdentifierType + { + get + { + if (IsKey(PartitionPack)) + { + return KeyIdentifier.PartitionPack; + } + if (IsKey(Preface)) + { + return KeyIdentifier.Preface; + } + if (IsKey(EssenceElement)) + { + return KeyIdentifier.EssenceElement; + } + if (IsKey(OperationalPattern)) + { + return KeyIdentifier.OperationalPattern; + } + if (IsKey(PartitionMetadata)) + { + return KeyIdentifier.PartitionMetadata; + } + if (IsKey(StructuralMetadata)) + { + return KeyIdentifier.StructuralMetadata; + } + if (IsKey(DataDefinitionVideo)) + { + return KeyIdentifier.DataDefinitionVideo; + } + if (IsKey(DataDefinitionAudio)) + { + return KeyIdentifier.DataDefinitionAudio; + } + if (IsKey(Primer)) + { + return KeyIdentifier.DataDefinitionAudio; + } + + return KeyIdentifier.Unknown; + } + } + + private bool IsKey(byte[] key) + { + if (KeySize != key.Length) + { + return false; + } + + for (int i = 0; i < KeySize; i++) + { + if (Key[i] != key[i] && key[i] != 0xff) + { + return false; + } + } + return true; + } + + public string DisplayKey + { + get + { + var sb = new StringBuilder(); + for (int i = 0; i < KeySize; i++) + { + sb.Append(Key[i].ToString("X2") + "-"); + } + return sb.ToString().TrimEnd('-'); + } + } + + } +} \ No newline at end of file diff --git a/libse/ContainerFormats/MaterialExchangeFormat/MxfParser.cs b/libse/ContainerFormats/MaterialExchangeFormat/MxfParser.cs new file mode 100644 index 000000000..2a45f8a6e --- /dev/null +++ b/libse/ContainerFormats/MaterialExchangeFormat/MxfParser.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Nikse.Core; + +namespace Nikse.SubtitleEdit.Core.ContainerFormats.MaterialExchangeFormat +{ + public class MxfParser + { + public string FileName { get; private set; } + public bool IsValid { get; private set; } + + private readonly List _subtitleList = new List(); + public List GetSubtitles() + { + return _subtitleList; + } + + private long _startPosition; + + public MxfParser(string fileName) + { + FileName = fileName; + using (var fs = new FileStream(FileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + ParseMxf(fs); + } + } + + public MxfParser(Stream stream) + { + FileName = null; + ParseMxf(stream); + } + + private void ParseMxf(Stream stream) + { + stream.Seek(0, SeekOrigin.Begin); + ReadHeaderPartitionPack(stream); + if (IsValid) + { + var length = stream.Length; + long next = _startPosition; + while (next + 20 < length) + { + stream.Seek(next, SeekOrigin.Begin); + var klv = new KlvPacket(stream); + + //Console.WriteLine(); + //Console.WriteLine("Key: " + klv.DisplayKey); + //Console.WriteLine("Type: " + klv.IdentifyerType); + //Console.WriteLine("Total size: " + klv.TotalSize); + //Console.WriteLine("Data position: " + klv.DataPosition); + //if (klv.IdentifyerType == KeyIdentifier.PartitionPack) + // Console.WriteLine("Partition status: " + klv.PartionStatus); + + next += klv.TotalSize; + + if (klv.IdentifierType == KeyIdentifier.EssenceElement && + klv.DataSize < 500000) + { + stream.Seek(klv.DataPosition, SeekOrigin.Begin); + var buffer = new byte[klv.DataSize]; + stream.Read(buffer, 0, buffer.Length); + string s = System.Text.Encoding.UTF8.GetString(buffer); + if (IsSubtitle(s)) + { + _subtitleList.Add(s); + } + } + } + } + } + + private bool IsSubtitle(string s) + { + if (s.Contains("\0")) + { + return false; + } + if (!s.Contains("xml") && !s.Contains(" --> ") && !s.Contains("00:00")) + { + return false; + } + + var list = new List(); + foreach (string line in s.Replace(Environment.NewLine, "\r").Replace("\n", "\r").Split('\r')) + { + list.Add(line); + } + var subtitle = new Subtitle(); + return subtitle.ReloadLoadSubtitle(list, null) != null; + } + + private void ReadHeaderPartitionPack(Stream stream) + { + IsValid = false; + var buffer = new byte[65536]; + var count = stream.Read(buffer, 0, buffer.Length); + if (count < 100) + { + return; + } + _startPosition = 0; + + for (int i = 0; i < count - 11; i++) + { + //Header Partition PackId = 06 0E 2B 34 02 05 01 01 0D 01 02 + if (buffer[i + 00] == 0x06 && // OID + buffer[i + 01] == 0x0E && // payload is 14 bytes + buffer[i + 02] == 0x2B && // 0x2B+34 lookup bytes + buffer[i + 03] == 0x34 && + buffer[i + 04] == 0x02 && + buffer[i + 05] == 0x05 && + buffer[i + 06] == 0x01 && + buffer[i + 07] == 0x01 && + buffer[i + 08] == 0x0D && + buffer[i + 09] == 0x01 && + buffer[i + 10] == 0x02) + { + _startPosition = i; + IsValid = true; + break; + } + } + } + + } +} \ No newline at end of file diff --git a/libse/ContainerFormats/MaterialExchangeFormat/PartitionStatus.cs b/libse/ContainerFormats/MaterialExchangeFormat/PartitionStatus.cs new file mode 100644 index 000000000..4a3cff4b2 --- /dev/null +++ b/libse/ContainerFormats/MaterialExchangeFormat/PartitionStatus.cs @@ -0,0 +1,11 @@ +namespace Nikse.SubtitleEdit.Core.ContainerFormats.MaterialExchangeFormat +{ + public enum PartitionStatus + { + Unknown = 0, + OpenAndIncomplete = 1, + ClosedAndIncomplete = 2, + OpenAndComplete = 3, + ClosedAndComplete = 4, + } +} diff --git a/libse/ContainerFormats/Matroska/MatroskaFile.cs b/libse/ContainerFormats/Matroska/MatroskaFile.cs new file mode 100644 index 000000000..3c76464c1 --- /dev/null +++ b/libse/ContainerFormats/Matroska/MatroskaFile.cs @@ -0,0 +1,649 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using Nikse.Core; +using Nikse.SubtitleEdit.Core.ContainerFormats.Ebml; + +namespace Nikse.SubtitleEdit.Core.ContainerFormats.Matroska +{ + public class MatroskaFile : IDisposable + { + public delegate void LoadMatroskaCallback(long position, long total); + + private readonly string _path; + private readonly FileStream _stream; + private readonly bool _valid; + private int _pixelWidth, _pixelHeight; + private double _frameRate; + private string _videoCodecId; + + private int _subtitleRipTrackNumber; + private List _subtitleRip = new List(); + private List _tracks; + + private readonly Element _segmentElement; + private long _timecodeScale = 1000000; + private double _duration; + + public MatroskaFile(string path) + { + _path = path; + _stream = new FastFileStream(path); + + // read header + var headerElement = ReadElement(); + if (headerElement != null && headerElement.Id == ElementId.Ebml) + { + // read segment + _stream.Seek(headerElement.DataSize, SeekOrigin.Current); + _segmentElement = ReadElement(); + if (_segmentElement != null && _segmentElement.Id == ElementId.Segment) + { + _valid = true; // matroska file must start with ebml header and segment + } + } + } + + public bool IsValid + { + get + { + return _valid; + } + } + + public string Path + { + get + { + return _path; + } + } + + public List GetTracks(bool subtitleOnly = false) + { + ReadSegmentInfoAndTracks(); + + if (_tracks == null) + return new List(); + + return subtitleOnly + ? _tracks.Where(t => t.IsSubtitle).ToList() + : _tracks; + } + + /// + /// Get first time of track + /// + /// Track number + /// Start time in milliseconds + public long GetTrackStartTime(int trackNumber) + { + // go to segment + _stream.Seek(_segmentElement.DataPosition, SeekOrigin.Begin); + + Element element; + while (_stream.Position < _stream.Length && (element = ReadElement()) != null) + { + switch (element.Id) + { + case ElementId.Info: + ReadInfoElement(element); + break; + case ElementId.Tracks: + ReadTracksElement(element); + break; + case ElementId.Cluster: + return FindTrackStartInCluster(element, trackNumber); + } + _stream.Seek(element.EndPosition, SeekOrigin.Begin); + } + + return 0; + } + + private long FindTrackStartInCluster(Element cluster, int targetTrackNumber) + { + long clusterTimeCode = 0L; + long trackStartTime = -1L; + bool done = false; + + Element element; + while (_stream.Position < cluster.EndPosition && (element = ReadElement()) != null && !done) + { + switch (element.Id) + { + case ElementId.None: + done = true; + break; + case ElementId.Timecode: + // Absolute timestamp of the cluster (based on TimecodeScale) + clusterTimeCode = (long)ReadUInt((int)element.DataSize); + break; + case ElementId.BlockGroup: + ReadBlockGroupElement(element, clusterTimeCode); + break; + case ElementId.SimpleBlock: + var trackNumber = (int)ReadVariableLengthUInt(); + if (trackNumber == targetTrackNumber) + { + // Timecode (relative to Cluster timecode, signed int16) + trackStartTime = ReadInt16(); + done = true; + } + break; + } + _stream.Seek(element.EndPosition, SeekOrigin.Begin); + } + + return (clusterTimeCode + trackStartTime) * _timecodeScale / 1000000; + } + + private void ReadVideoElement(Element videoElement) + { + Element element; + while (_stream.Position < videoElement.EndPosition && (element = ReadElement()) != null) + { + switch (element.Id) + { + case ElementId.PixelWidth: + _pixelWidth = (int)ReadUInt((int)element.DataSize); + break; + case ElementId.PixelHeight: + _pixelHeight = (int)ReadUInt((int)element.DataSize); + break; + default: + _stream.Seek(element.DataSize, SeekOrigin.Current); + break; + } + } + } + + private void ReadTrackEntryElement(Element trackEntryElement) + { + long defaultDuration = 0; + bool isVideo = false; + bool isAudio = false; + bool isSubtitle = false; + var trackNumber = 0; + string name = string.Empty; + string language = string.Empty; + string codecId = string.Empty; + string codecPrivate = string.Empty; + //var biCompression = string.Empty; + int contentCompressionAlgorithm = -1; + int contentEncodingType = -1; + + Element element; + while (_stream.Position < trackEntryElement.EndPosition && (element = ReadElement()) != null) + { + switch (element.Id) + { + case ElementId.DefaultDuration: + defaultDuration = (int)ReadUInt((int)element.DataSize); + break; + case ElementId.Video: + ReadVideoElement(element); + isVideo = true; + break; + case ElementId.Audio: + isAudio = true; + break; + case ElementId.TrackNumber: + trackNumber = (int)ReadUInt((int)element.DataSize); + break; + case ElementId.Name: + name = ReadString((int)element.DataSize, Encoding.UTF8); + break; + case ElementId.Language: + language = ReadString((int)element.DataSize, Encoding.ASCII); + break; + case ElementId.CodecId: + codecId = ReadString((int)element.DataSize, Encoding.ASCII); + break; + case ElementId.TrackType: + switch (_stream.ReadByte()) + { + case 1: + isVideo = true; + break; + case 2: + isAudio = true; + break; + case 17: + isSubtitle = true; + break; + } + break; + case ElementId.CodecPrivate: + codecPrivate = ReadString((int)element.DataSize, Encoding.UTF8); + //if (codecPrivate.Length > 20) + // biCompression = codecPrivate.Substring(16, 4); + break; + case ElementId.ContentEncodings: + contentCompressionAlgorithm = 0; // default value + contentEncodingType = 0; // default value + + var contentEncodingElement = ReadElement(); + if (contentEncodingElement != null && contentEncodingElement.Id == ElementId.ContentEncoding) + { + ReadContentEncodingElement(element, ref contentCompressionAlgorithm, ref contentEncodingType); + } + break; + } + _stream.Seek(element.EndPosition, SeekOrigin.Begin); + } + + _tracks.Add(new MatroskaTrackInfo + { + TrackNumber = trackNumber, + IsVideo = isVideo, + IsAudio = isAudio, + IsSubtitle = isSubtitle, + Language = language, + CodecId = codecId, + CodecPrivate = codecPrivate, + Name = name, + ContentEncodingType = contentEncodingType, + ContentCompressionAlgorithm = contentCompressionAlgorithm + }); + + if (isVideo) + { + if (defaultDuration > 0) + { + _frameRate = 1.0 / (defaultDuration / 1000000000.0); + } + _videoCodecId = codecId; + } + } + + private void ReadContentEncodingElement(Element contentEncodingElement, ref int contentCompressionAlgorithm, ref int contentEncodingType) + { + Element element; + while (_stream.Position < contentEncodingElement.EndPosition && (element = ReadElement()) != null) + { + switch (element.Id) + { + case ElementId.ContentEncodingOrder: + var contentEncodingOrder = ReadUInt((int)element.DataSize); + System.Diagnostics.Debug.WriteLine("ContentEncodingOrder: " + contentEncodingOrder); + break; + case ElementId.ContentEncodingScope: + var contentEncodingScope = ReadUInt((int)element.DataSize); + System.Diagnostics.Debug.WriteLine("ContentEncodingScope: " + contentEncodingScope); + break; + case ElementId.ContentEncodingType: + contentEncodingType = (int)ReadUInt((int)element.DataSize); + break; + case ElementId.ContentCompression: + Element compElement; + while (_stream.Position < element.EndPosition && (compElement = ReadElement()) != null) + { + switch (compElement.Id) + { + case ElementId.ContentCompAlgo: + contentCompressionAlgorithm = (int)ReadUInt((int)compElement.DataSize); + break; + case ElementId.ContentCompSettings: + var contentCompSettings = ReadUInt((int)compElement.DataSize); + System.Diagnostics.Debug.WriteLine("ContentCompSettings: " + contentCompSettings); + break; + default: + _stream.Seek(element.DataSize, SeekOrigin.Current); + break; + } + } + break; + default: + _stream.Seek(element.DataSize, SeekOrigin.Current); + break; + } + } + } + + private void ReadInfoElement(Element infoElement) + { + Element element; + while (_stream.Position < infoElement.EndPosition && (element = ReadElement()) != null) + { + switch (element.Id) + { + case ElementId.TimecodeScale: + // Timestamp scale in nanoseconds (1.000.000 means all timestamps in the segment are expressed in milliseconds) + _timecodeScale = (int)ReadUInt((int)element.DataSize); + break; + case ElementId.Duration: + // Duration of the segment (based on TimecodeScale) + _duration = element.DataSize == 4 ? ReadFloat32() : ReadFloat64(); + _duration /= _timecodeScale * 1000000.0; + break; + default: + _stream.Seek(element.DataSize, SeekOrigin.Current); + break; + } + } + } + + private void ReadTracksElement(Element tracksElement) + { + _tracks = new List(); + + Element element; + while (_stream.Position < tracksElement.EndPosition && (element = ReadElement()) != null) + { + if (element.Id == ElementId.TrackEntry) + { + ReadTrackEntryElement(element); + } + else + { + _stream.Seek(element.DataSize, SeekOrigin.Current); + } + } + } + + /// Duration of the segment in milliseconds. + public void GetInfo(out double frameRate, out int pixelWidth, out int pixelHeight, out double duration, out string videoCodec) + { + ReadSegmentInfoAndTracks(); + + pixelWidth = _pixelWidth; + pixelHeight = _pixelHeight; + frameRate = _frameRate; + duration = _duration; + videoCodec = _videoCodecId; + } + + private void ReadCluster(Element clusterElement) + { + long clusterTimeCode = 0; + + Element element; + while (_stream.Position < clusterElement.EndPosition && (element = ReadElement()) != null) + { + switch (element.Id) + { + case ElementId.Timecode: + clusterTimeCode = (long)ReadUInt((int)element.DataSize); + break; + case ElementId.BlockGroup: + ReadBlockGroupElement(element, clusterTimeCode); + break; + case ElementId.SimpleBlock: + var subtitle = ReadSubtitleBlock(element, clusterTimeCode); + if (subtitle != null) + { + _subtitleRip.Add(subtitle); + } + break; + default: + _stream.Seek(element.DataSize, SeekOrigin.Current); + break; + } + } + } + + private void ReadBlockGroupElement(Element clusterElement, long clusterTimeCode) + { + MatroskaSubtitle subtitle = null; + + Element element; + while (_stream.Position < clusterElement.EndPosition && (element = ReadElement()) != null) + { + switch (element.Id) + { + case ElementId.Block: + subtitle = ReadSubtitleBlock(element, clusterTimeCode); + if (subtitle == null) + { + return; + } + _subtitleRip.Add(subtitle); + break; + case ElementId.BlockDuration: + var duration = (long)ReadUInt((int)element.DataSize); + if (subtitle != null) + { + subtitle.Duration = duration; + } + break; + default: + _stream.Seek(element.DataSize, SeekOrigin.Current); + break; + } + } + } + + private MatroskaSubtitle ReadSubtitleBlock(Element blockElement, long clusterTimeCode) + { + var trackNumber = (int)ReadVariableLengthUInt(); + if (trackNumber != _subtitleRipTrackNumber) + { + _stream.Seek(blockElement.EndPosition, SeekOrigin.Begin); + return null; + } + + var timeCode = ReadInt16(); + + // lacing + var flags = (byte)_stream.ReadByte(); + int frames; + switch (flags & 6) + { + case 0: // 00000000 = No lacing + System.Diagnostics.Debug.Print("No lacing"); + break; + case 2: // 00000010 = Xiph lacing + frames = _stream.ReadByte() + 1; + System.Diagnostics.Debug.Print("Xiph lacing ({0} frames)", frames); + break; + case 4: // 00000100 = Fixed-size lacing + frames = _stream.ReadByte() + 1; + for (var i = 0; i < frames; i++) + { + _stream.ReadByte(); // frames + } + System.Diagnostics.Debug.Print("Fixed-size lacing ({0} frames)", frames); + break; + case 6: // 00000110 = EMBL lacing + frames = _stream.ReadByte() + 1; + System.Diagnostics.Debug.Print("EBML lacing ({0} frames)", frames); + break; + } + + // save subtitle data + var dataLength = (int)(blockElement.EndPosition - _stream.Position); + var data = new byte[dataLength]; + _stream.Read(data, 0, dataLength); + + return new MatroskaSubtitle(data, clusterTimeCode + timeCode); + } + + public List GetSubtitle(int trackNumber, LoadMatroskaCallback progressCallback) + { + _subtitleRipTrackNumber = trackNumber; + ReadSegmentCluster(progressCallback); + return _subtitleRip; + } + + public void Dispose() + { + if (_stream != null) + { + _stream.Dispose(); + } + } + + private void ReadSegmentInfoAndTracks() + { + // go to segment + _stream.Seek(_segmentElement.DataPosition, SeekOrigin.Begin); + + Element element; + while (_stream.Position < _segmentElement.EndPosition && (element = ReadElement()) != null) + { + switch (element.Id) + { + case ElementId.Info: + ReadInfoElement(element); + break; + case ElementId.Tracks: + ReadTracksElement(element); + return; + default: + _stream.Seek(element.DataSize, SeekOrigin.Current); + break; + } + } + } + + private void ReadSegmentCluster(LoadMatroskaCallback progressCallback) + { + // go to segment + _stream.Seek(_segmentElement.DataPosition, SeekOrigin.Begin); + + Element element; + while (_stream.Position < _segmentElement.EndPosition && (element = ReadElement()) != null) + { + if (element.Id == ElementId.Cluster) + { + ReadCluster(element); + } + else + { + _stream.Seek(element.DataSize, SeekOrigin.Current); + } + + if (progressCallback != null) + { + progressCallback.Invoke(element.EndPosition, _stream.Length); + } + } + } + + private Element ReadElement() + { + var id = (ElementId)ReadVariableLengthUInt(false); + if (id <= ElementId.None) + { + return null; + } + + var size = (long)ReadVariableLengthUInt(); + return new Element(id, _stream.Position, size); + } + + private ulong ReadVariableLengthUInt(bool unsetFirstBit = true) + { + // Begin loop with byte set to newly read byte + var first = _stream.ReadByte(); + var length = 0; + + // Begin by counting the bits unset before the highest set bit + var mask = 0x80; + for (var i = 0; i < 8; i++) + { + // Start at left, shift to right + if ((first & mask) == mask) + { + length = i + 1; + break; + } + mask >>= 1; + } + if (length == 0) + { + return 0; + } + + // Read remaining big endian bytes and convert to 64-bit unsigned integer. + var result = (ulong)(unsetFirstBit ? first & (0xFF >> length) : first); + result <<= --length * 8; + for (var i = 1; i <= length; i++) + { + result |= (ulong)_stream.ReadByte() << (length - i) * 8; + } + return result; + } + + /// + /// Reads a fixed length unsigned integer from the current stream and advances the current + /// position of the stream by the integer length in bytes. + /// + /// The length in bytes of the integer. + /// A 64-bit unsigned integer. + private ulong ReadUInt(int length) + { + var data = new byte[length]; + _stream.Read(data, 0, length); + + // Convert the big endian byte array to a 64-bit unsigned integer. + var result = 0UL; + var shift = 0; + for (var i = length - 1; i >= 0; i--) + { + result |= (ulong)data[i] << shift; + shift += 8; + } + return result; + } + + /// + /// Reads a 2-byte signed integer from the current stream and advances the current position + /// of the stream by two bytes. + /// + /// A 2-byte signed integer read from the current stream. + private short ReadInt16() + { + var data = new byte[2]; + _stream.Read(data, 0, 2); + return (short)(data[0] << 8 | data[1]); + } + + /// + /// Reads a 4-byte floating point value from the current stream and advances the current + /// position of the stream by four bytes. + /// + /// A 4-byte floating point value read from the current stream. + private unsafe float ReadFloat32() + { + var data = new byte[4]; + _stream.Read(data, 0, 4); + + var result = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; + return *(float*)&result; + } + + /// + /// Reads a 8-byte floating point value from the current stream and advances the current + /// position of the stream by eight bytes. + /// + /// A 8-byte floating point value read from the current stream. + private unsafe double ReadFloat64() + { + var data = new byte[8]; + _stream.Read(data, 0, 8); + + var lo = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; + var hi = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]; + var result = (uint)hi | (long)lo << 32; + return *(double*)&result; + } + + /// + /// Reads a fixed length string from the current stream using the specified encoding. + /// + /// The length in bytes of the string. + /// The encoding of the string. + /// The string being read. + private string ReadString(int length, Encoding encoding) + { + var buffer = new byte[length]; + _stream.Read(buffer, 0, length); + return encoding.GetString(buffer); + } + } +} \ No newline at end of file diff --git a/libse/ContainerFormats/Matroska/MatroskaSubtitle.cs b/libse/ContainerFormats/Matroska/MatroskaSubtitle.cs new file mode 100644 index 000000000..00ebba6cd --- /dev/null +++ b/libse/ContainerFormats/Matroska/MatroskaSubtitle.cs @@ -0,0 +1,41 @@ +using System; + +namespace Nikse.SubtitleEdit.Core.ContainerFormats.Matroska +{ + public class MatroskaSubtitle + { + public byte[] Data { get; set; } + public long Start { get; set; } + public long Duration { get; set; } + + public MatroskaSubtitle(byte[] data, long start, long duration) + { + Data = data; + Start = start; + Duration = duration; + } + + public MatroskaSubtitle(byte[] data, long start) + : this(data, start, 0) + { + } + + public long End + { + get + { + return Start + Duration; + } + } + + public string Text + { + get + { + if (Data != null) + return System.Text.Encoding.UTF8.GetString(Data).Replace("\\N", Environment.NewLine); + return string.Empty; + } + } + } +} diff --git a/libse/ContainerFormats/Matroska/MatroskaTrackInfo.cs b/libse/ContainerFormats/Matroska/MatroskaTrackInfo.cs new file mode 100644 index 000000000..d39fc1ae8 --- /dev/null +++ b/libse/ContainerFormats/Matroska/MatroskaTrackInfo.cs @@ -0,0 +1,19 @@ +namespace Nikse.SubtitleEdit.Core.ContainerFormats.Matroska +{ + public class MatroskaTrackInfo + { + public int TrackNumber { get; set; } + public string Uid { get; set; } + public bool IsVideo { get; set; } + public bool IsAudio { get; set; } + public bool IsSubtitle { get; set; } + public string CodecId { get; set; } + public string CodecPrivate { get; set; } + public int DefaultDuration { get; set; } + public string Language { get; set; } + + public string Name { get; set; } + public int ContentCompressionAlgorithm { get; set; } + public int ContentEncodingType { get; set; } + } +} diff --git a/libse/ContainerFormats/Mp4/Boxes/Box.cs b/libse/ContainerFormats/Mp4/Boxes/Box.cs new file mode 100644 index 000000000..e8d139a4c --- /dev/null +++ b/libse/ContainerFormats/Mp4/Boxes/Box.cs @@ -0,0 +1,82 @@ +using System; +using System.Text; + +namespace Nikse.SubtitleEdit.Core.ContainerFormats.Mp4.Boxes +{ + public class Box + { + public byte[] Buffer; + public ulong Position; + public string Name; + public UInt64 Size; + + public static uint GetUInt(byte[] buffer, int index) + { + return (uint)((buffer[index] << 24) + (buffer[index + 1] << 16) + (buffer[index + 2] << 8) + buffer[index + 3]); + } + + public uint GetUInt(int index) + { + return (uint)((Buffer[index] << 24) + (Buffer[index + 1] << 16) + (Buffer[index + 2] << 8) + Buffer[index + 3]); + } + + public UInt64 GetUInt64(int index) + { + return (UInt64)Buffer[index] << 56 | (UInt64)Buffer[index + 1] << 48 | (UInt64)Buffer[index + 2] << 40 | (UInt64)Buffer[index + 3] << 32 | + (UInt64)Buffer[index + 4] << 24 | (UInt64)Buffer[index + 5] << 16 | (UInt64)Buffer[index + 6] << 8 | Buffer[index + 7]; + } + + public static UInt64 GetUInt64(byte[] buffer, int index) + { + return (UInt64)buffer[index] << 56 | (UInt64)buffer[index + 1] << 48 | (UInt64)buffer[index + 2] << 40 | (UInt64)buffer[index + 3] << 32 | + (UInt64)buffer[index + 4] << 24 | (UInt64)buffer[index + 5] << 16 | (UInt64)buffer[index + 6] << 8 | buffer[index + 7]; + } + + public static int GetWord(byte[] buffer, int index) + { + return (buffer[index] << 8) + buffer[index + 1]; + } + + public int GetWord(int index) + { + return (Buffer[index] << 8) + Buffer[index + 1]; + } + + public string GetString(int index, int count) + { + return Encoding.UTF8.GetString(Buffer, index, count); + } + + public static string GetString(byte[] buffer, int index, int count) + { + if (count <= 0) + return string.Empty; + return Encoding.UTF8.GetString(buffer, index, count); + } + + internal bool InitializeSizeAndName(System.IO.FileStream fs) + { + Buffer = new byte[8]; + var bytesRead = fs.Read(Buffer, 0, Buffer.Length); + if (bytesRead < Buffer.Length) + return false; + Size = GetUInt(0); + Name = GetString(4, 4); + + if (Size == 0) + { + Size = (UInt64)(fs.Length - fs.Position); + } + if (Size == 1) + { + bytesRead = fs.Read(Buffer, 0, Buffer.Length); + if (bytesRead < Buffer.Length) + return false; + Size = GetUInt64(0) - 8; + } + Position = ((ulong)(fs.Position)) + Size - 8; + return true; + } + + } +} diff --git a/libse/ContainerFormats/Mp4/Boxes/Mdhd.cs b/libse/ContainerFormats/Mp4/Boxes/Mdhd.cs new file mode 100644 index 000000000..5f4667b3d --- /dev/null +++ b/libse/ContainerFormats/Mp4/Boxes/Mdhd.cs @@ -0,0 +1,489 @@ +using System; +using System.Globalization; +using System.IO; + +namespace Nikse.SubtitleEdit.Core.ContainerFormats.Mp4.Boxes +{ + public class Mdhd : Box + { + + public readonly UInt64 CreationTime; + public readonly UInt64 ModificationTime; + public readonly UInt32 TimeScale; + public readonly UInt64 Duration; + public readonly string Iso639ThreeLetterCode; + public readonly int Quality; + + public Mdhd(FileStream fs, ulong size) + { + Buffer = new byte[size - 4]; + fs.Read(Buffer, 0, Buffer.Length); + int languageIndex = 20; + int version = Buffer[0]; + if (version == 0) + { + CreationTime = GetUInt(4); + ModificationTime = GetUInt(8); + TimeScale = GetUInt(12); + Duration = GetUInt(16); + Quality = GetWord(22); + } + else + { + CreationTime = GetUInt64(4); + ModificationTime = GetUInt64(12); + TimeScale = GetUInt(16); + Duration = GetUInt64(20); + languageIndex = 24; + Quality = GetWord(26); + } + + // language code = skip first byte, 5 bytes + 5 bytes + 5 bytes (add 0x60 to get ascii value) + int languageByte = ((Buffer[languageIndex] << 1) >> 3) + 0x60; + int languageByte2 = ((Buffer[languageIndex] & 0x3) << 3) + (Buffer[languageIndex + 1] >> 5) + 0x60; + int languageByte3 = (Buffer[languageIndex + 1] & 0x1f) + 0x60; + char x = (char)languageByte; + char x2 = (char)languageByte2; + char x3 = (char)languageByte3; + Iso639ThreeLetterCode = x.ToString(CultureInfo.InvariantCulture) + x2.ToString(CultureInfo.InvariantCulture) + x3.ToString(CultureInfo.InvariantCulture); + } + + public string LanguageString + { + get + { + switch (Iso639ThreeLetterCode) + { + case ("abk"): return "Abkhazian"; + case ("ace"): return "Achinese"; + case ("ach"): return "Acoli"; + case ("ada"): return "Adangme"; + case ("aar"): return "Afar"; + case ("afh"): return "Afrihili"; + case ("afr"): return "Afrikaans"; + case ("afa"): return "Afro-Asiatic (Other)"; + case ("aka"): return "Akan"; + case ("akk"): return "Akkadian"; + case ("alb"): return "Albanian"; + case ("sqi"): return "Albanian"; + case ("ale"): return "Aleut"; + case ("alg"): return "Algonquian languages"; + case ("tut"): return "Altaic (Other)"; + case ("amh"): return "Amharic"; + case ("apa"): return "Apache languages"; + case ("ara"): return "Arabic"; + case ("arc"): return "Aramaic"; + case ("arg"): return "Arabic"; + case ("arp"): return "Arapaho"; + case ("arn"): return "Araucanian"; + case ("arw"): return "Arawak"; + case ("arm"): return "Armenian"; + case ("hye"): return "Armenian"; + case ("art"): return "Artificial (Other)"; + case ("asm"): return "Assamese"; + case ("ava"): return "Avaric"; + case ("ath"): return "Athapascan languages"; + case ("ave"): return "Avestan"; + case ("awa"): return "Awadhi"; + case ("aym"): return "Aymara"; + case ("aze"): return "Azerbaijani"; + case ("nah"): return "Aztec"; + case ("ban"): return "Balinese"; + case ("bat"): return "Baltic (Other)"; + case ("bal"): return "Baluchi"; + case ("bam"): return "Bambara"; + case ("bai"): return "Bamileke languages"; + case ("bad"): return "Banda"; + case ("bnt"): return "Bantu (Other)"; + case ("bas"): return "Basa"; + case ("bak"): return "Bashkir"; + case ("baq"): return "Basque"; + case ("eus"): return "Basque"; + case ("bej"): return "Beja"; + case ("bem"): return "Bemba"; + case ("ben"): return "Bengali"; + case ("ber"): return "Berber (Other)"; + case ("bho"): return "Bhojpuri"; + case ("bih"): return "Bihari"; + case ("bik"): return "Bikol"; + case ("bin"): return "Bini"; + case ("bis"): return "Bislama"; + case ("bra"): return "Braj"; + case ("bre"): return "Breton"; + case ("bug"): return "Buginese"; + case ("bul"): return "Bulgarian"; + case ("bua"): return "Buriat"; + case ("bur"): return "Burmese"; + case ("mya"): return "Burmese"; + case ("bel"): return "Byelorussian"; + case ("cad"): return "Caddo"; + case ("car"): return "Carib"; + case ("cat"): return "Catalan"; + case ("cau"): return "Caucasian (Other)"; + case ("ceb"): return "Cebuano"; + case ("cel"): return "Celtic (Other)"; + case ("cai"): return "Central American Indian (Other)"; + case ("chg"): return "Chagatai"; + case ("cha"): return "Chamorro"; + case ("che"): return "Chechen"; + case ("chr"): return "Cherokee"; + case ("chy"): return "Cheyenne"; + case ("chb"): return "Chibcha"; + case ("chi"): return "Chinese"; + case ("zho"): return "Chinese"; + case ("chn"): return "Chinook jargon"; + case ("cho"): return "Choctaw"; + case ("chu"): return "Church Slavic"; + case ("chv"): return "Chuvash"; + case ("cop"): return "Coptic"; + case ("cor"): return "Cornish"; + case ("cos"): return "Corsican"; + case ("cre"): return "Cree"; + case ("mus"): return "Creek"; + case ("crp"): return "Creoles and Pidgins (Other)"; + case ("cpe"): return "Creoles and Pidgins, English-based (Other)"; + case ("cpf"): return "Creoles and Pidgins, French-based (Other)"; + case ("cpp"): return "Creoles and Pidgins, Portuguese-based (Other)"; + case ("cus"): return "Cushitic (Other)"; + case (" "): return "Croatian"; + case ("ces"): return "Czech"; + case ("cze"): return "Czech"; + case ("dak"): return "Dakota"; + case ("dan"): return "Danish"; + case ("del"): return "Delaware"; + case ("din"): return "Dinka"; + case ("div"): return "Divehi"; + case ("doi"): return "Dogri"; + case ("dra"): return "Dravidian (Other)"; + case ("dua"): return "Duala"; + case ("dut"): return "Dutch"; + case ("nla"): return "Dutch"; + case ("dum"): return "Dutch, Middle (ca. 1050-1350)"; + case ("dyu"): return "Dyula"; + case ("dzo"): return "Dzongkha"; + case ("efi"): return "Efik"; + case ("egy"): return "Egyptian (Ancient)"; + case ("eka"): return "Ekajuk"; + case ("elx"): return "Elamite"; + case ("eng"): return "English"; + case ("enm"): return "English, Middle (ca. 1100-1500)"; + case ("ang"): return "English, Old (ca. 450-1100)"; + case ("esk"): return "Eskimo (Other)"; + case ("epo"): return "Esperanto"; + case ("est"): return "Estonian"; + case ("ewe"): return "Ewe"; + case ("ewo"): return "Ewondo"; + case ("fan"): return "Fang"; + case ("fat"): return "Fanti"; + case ("fao"): return "Faroese"; + case ("fij"): return "Fijian"; + case ("fin"): return "Finnish"; + case ("fiu"): return "Finno-Ugrian (Other)"; + case ("fon"): return "Fon"; + case ("fra"): return "French"; + case ("fre"): return "French"; + case ("frm"): return "French, Middle (ca. 1400-1600)"; + case ("fro"): return "French, Old (842- ca. 1400)"; + case ("fry"): return "Frisian"; + case ("ful"): return "Fulah"; + case ("gaa"): return "Ga"; + case ("gae"): return "Gaelic (Scots)"; + case ("gdh"): return "Gaelic (Scots)"; + case ("glg"): return "Gallegan"; + case ("lug"): return "Ganda"; + case ("gay"): return "Gayo"; + case ("gez"): return "Geez"; + case ("geo"): return "Georgian"; + case ("kat"): return "Georgian"; + case ("deu"): return "German"; + case ("ger"): return "German"; + case ("gmh"): return "German, Middle High (ca. 1050-1500)"; + case ("goh"): return "German, Old High (ca. 750-1050)"; + case ("gem"): return "Germanic (Other)"; + case ("gil"): return "Gilbertese"; + case ("gon"): return "Gondi"; + case ("got"): return "Gothic"; + case ("grb"): return "Grebo"; + case ("grc"): return "Greek, Ancient (to 1453)"; + case ("ell"): return "Greek, Modern (1453-)"; + case ("gre"): return "Greek, Modern (1453-)"; + case ("kal"): return "Greenlandic"; + case ("grn"): return "Guarani"; + case ("guj"): return "Gujarati"; + case ("hai"): return "Haida"; + case ("hau"): return "Hausa"; + case ("haw"): return "Hawaiian"; + case ("heb"): return "Hebrew"; + case ("her"): return "Herero"; + case ("hil"): return "Hiligaynon"; + case ("him"): return "Himachali"; + case ("hin"): return "Hindi"; + case ("hmo"): return "Hiri Motu"; + case ("hun"): return "Hungarian"; + case ("hup"): return "Hupa"; + case ("iba"): return "Iban"; + case ("ice"): return "Icelandic"; + case ("ibo"): return "Igbo"; + case ("ijo"): return "Ijo"; + case ("ilo"): return "Iloko"; + case ("inc"): return "Indic (Other)"; + case ("ine"): return "Indo-European (Other)"; + case ("ind"): return "Indonesian"; + case ("ina"): return "Interlingua (International Auxiliary language Association)"; + // case ("ine"): return "Interlingue"; + case ("iku"): return "Inuktitut"; + case ("ipk"): return "Inupiak"; + case ("ira"): return "Iranian (Other)"; + case ("gai"): return "Irish"; + case ("iri"): return "Irish"; + case ("sga"): return "Irish, Old (to 900)"; + case ("mga"): return "Irish, Middle (900 - 1200)"; + case ("iro"): return "Iroquoian languages"; + case ("ita"): return "Italian"; + case ("jpn"): return "Japanese"; + case ("jav"): return "Javanese"; + case ("jaw"): return "Javanese"; + case ("jrb"): return "Judeo-Arabic"; + case ("jpr"): return "Judeo-Persian"; + case ("kab"): return "Kabyle"; + case ("kac"): return "Kachin"; + case ("kam"): return "Kamba"; + case ("kan"): return "Kannada"; + case ("kau"): return "Kanuri"; + case ("kaa"): return "Kara-Kalpak"; + case ("kar"): return "Karen"; + case ("kas"): return "Kashmiri"; + case ("kaw"): return "Kawi"; + case ("kaz"): return "Kazakh"; + case ("kha"): return "Khasi"; + case ("khm"): return "Khmer"; + case ("khi"): return "Khoisan (Other)"; + case ("kho"): return "Khotanese"; + case ("kik"): return "Kikuyu"; + case ("kin"): return "Kinyarwanda"; + case ("kir"): return "Kirghiz"; + case ("kom"): return "Komi"; + case ("kon"): return "Kongo"; + case ("kok"): return "Konkani"; + case ("kor"): return "Korean"; + case ("kpe"): return "Kpelle"; + case ("kro"): return "Kru"; + case ("kua"): return "Kuanyama"; + case ("kum"): return "Kumyk"; + case ("kur"): return "Kurdish"; + case ("kru"): return "Kurukh"; + case ("kus"): return "Kusaie"; + case ("kut"): return "Kutenai"; + case ("lad"): return "Ladino"; + case ("lah"): return "Lahnda"; + case ("lam"): return "Lamba"; + case ("oci"): return "Langue d'Oc (post 1500)"; + case ("lao"): return "Lao"; + case ("lat"): return "Latin"; + case ("lav"): return "Latvian"; + case ("ltz"): return "Letzeburgesch"; + case ("lez"): return "Lezghian"; + case ("lin"): return "Lingala"; + case ("lit"): return "Lithuanian"; + case ("loz"): return "Lozi"; + case ("lub"): return "Luba-Katanga"; + case ("lui"): return "Luiseno"; + case ("lun"): return "Lunda"; + case ("luo"): return "Luo (Kenya and Tanzania)"; + case ("mac"): return "Macedonian"; + case ("mad"): return "Madurese"; + case ("mag"): return "Magahi"; + case ("mai"): return "Maithili"; + case ("mak"): return "Makasar"; + case ("mlg"): return "Malagasy"; + case ("may"): return "Malay"; + case ("msa"): return "Malay"; + case ("mal"): return "Malayalam"; + case ("mlt"): return "Maltese"; + case ("man"): return "Mandingo"; + case ("mni"): return "Manipuri"; + case ("mno"): return "Manobo languages"; + case ("max"): return "Manx"; + case ("mao"): return "Maori"; + case ("mri"): return "Maori"; + case ("mar"): return "Marathi"; + case ("chm"): return "Mari"; + case ("mah"): return "Marshall"; + case ("mwr"): return "Marwari"; + case ("mas"): return "Masai"; + case ("myn"): return "Mayan languages"; + case ("men"): return "Mende"; + case ("mic"): return "Micmac"; + case ("min"): return "Minangkabau"; + case ("mis"): return "Miscellaneous (Other)"; + case ("moh"): return "Mohawk"; + case ("mol"): return "Moldavian"; + case ("mkh"): return "Mon-Kmer (Other)"; + case ("lol"): return "Mongo"; + case ("mon"): return "Mongolian"; + case ("mos"): return "Mossi"; + case ("mul"): return "Multiple languages"; + case ("mun"): return "Munda languages"; + case ("nau"): return "Nauru"; + case ("nav"): return "Navajo"; + case ("nde"): return "Ndebele, North"; + case ("nbl"): return "Ndebele, South"; + case ("ndo"): return "Ndongo"; + case ("nep"): return "Nepali"; + case ("new"): return "Newari"; + case ("nic"): return "Niger-Kordofanian (Other)"; + case ("ssa"): return "Nilo-Saharan (Other)"; + case ("niu"): return "Niuean"; + case ("non"): return "Norse, Old"; + case ("nai"): return "North American Indian (Other)"; + case ("nor"): return "Norwegian"; + case ("nob"): return "Norwegian (Bokmål)"; + case ("nno"): return "Norwegian (Nynorsk)"; + case ("nub"): return "Nubian languages"; + case ("nym"): return "Nyamwezi"; + case ("nya"): return "Nyanja"; + case ("nyn"): return "Nyankole"; + case ("nyo"): return "Nyoro"; + case ("nzi"): return "Nzima"; + case ("oji"): return "Ojibwa"; + case ("ori"): return "Oriya"; + case ("orm"): return "Oromo"; + case ("osa"): return "Osage"; + case ("oss"): return "Ossetic"; + case ("oto"): return "Otomian languages"; + case ("pal"): return "Pahlavi"; + case ("pau"): return "Palauan"; + case ("pli"): return "Pali"; + case ("pam"): return "Pampanga"; + case ("pag"): return "Pangasinan"; + case ("pan"): return "Panjabi"; + case ("pap"): return "Papiamento"; + case ("paa"): return "Papuan-Australian (Other)"; + case ("fas"): return "Persian"; + case ("per"): return "Persian"; + case ("peo"): return "Persian, Old (ca 600 - 400 B.C.)"; + case ("phn"): return "Phoenician"; + case ("pol"): return "Polish"; + case ("pon"): return "Ponape"; + case ("por"): return "Portuguese"; + case ("pra"): return "Prakrit languages"; + case ("pro"): return "Provencal, Old (to 1500)"; + case ("pus"): return "Pushto"; + case ("que"): return "Quechua"; + case ("roh"): return "Rhaeto-Romance"; + case ("raj"): return "Rajasthani"; + case ("rar"): return "Rarotongan"; + case ("roa"): return "Romance (Other)"; + case ("ron"): return "Romanian"; + case ("rum"): return "Romanian"; + case ("rom"): return "Romany"; + case ("run"): return "Rundi"; + case ("rus"): return "Russian"; + case ("sal"): return "Salishan languages"; + case ("sam"): return "Samaritan Aramaic"; + case ("smi"): return "Sami languages"; + case ("smo"): return "Samoan"; + case ("sad"): return "Sandawe"; + case ("sag"): return "Sango"; + case ("san"): return "Sanskrit"; + case ("srd"): return "Sardinian"; + case ("sco"): return "Scots"; + case ("sel"): return "Selkup"; + case ("sem"): return "Semitic (Other)"; + case ("srp"): return "Serbian"; + case ("scr"): return "Serbo-Croatian"; + case ("srr"): return "Serer"; + case ("shn"): return "Shan"; + case ("sna"): return "Shona"; + case ("sid"): return "Sidamo"; + case ("bla"): return "Siksika"; + case ("snd"): return "Sindhi"; + case ("sin"): return "Singhalese"; + case ("sit"): return "Sino-Tibetan (Other)"; + case ("sio"): return "Siouan languages"; + case ("sla"): return "Slavic (Other)"; + //case ("ssw"): return "Siswant"; + case ("slk"): return "Slovak"; + case ("slv"): return "Slovenian"; + case ("sog"): return "Sogdian"; + case ("som"): return "Somali"; + case ("son"): return "Songhai"; + case ("wen"): return "Sorbian languages"; + case ("nso"): return "Sotho, Northern"; + case ("sot"): return "Sotho, Southern"; + case ("sai"): return "South American Indian (Other)"; + case ("esl"): return "Spanish"; + case ("spa"): return "Spanish"; + case ("suk"): return "Sukuma"; + case ("sux"): return "Sumerian"; + case ("sun"): return "Sudanese"; + case ("sus"): return "Susu"; + case ("swa"): return "Swahili"; + case ("ssw"): return "Swazi"; + case ("sve"): return "Swedish"; + case ("swe"): return "Swedish"; + case ("syr"): return "Syriac"; + case ("tgl"): return "Tagalog"; + case ("tah"): return "Tahitian"; + case ("tgk"): return "Tajik"; + case ("tmh"): return "Tamashek"; + case ("tam"): return "Tamil"; + case ("tat"): return "Tatar"; + case ("tel"): return "Telugu"; + case ("ter"): return "Tereno"; + case ("tha"): return "Thai"; + case ("bod"): return "Tibetan"; + case ("tib"): return "Tibetan"; + case ("tig"): return "Tigre"; + case ("tir"): return "Tigrinya"; + case ("tem"): return "Timne"; + case ("tiv"): return "Tivi"; + case ("tli"): return "Tlingit"; + case ("tog"): return "Tonga (Nyasa)"; + case ("ton"): return "Tonga (Tonga Islands)"; + case ("tru"): return "Truk"; + case ("tsi"): return "Tsimshian"; + case ("tso"): return "Tsonga"; + case ("tsn"): return "Tswana"; + case ("tum"): return "Tumbuka"; + case ("tur"): return "Turkish"; + case ("ota"): return "Turkish, Ottoman (1500 - 1928)"; + case ("tuk"): return "Turkmen"; + case ("tyv"): return "Tuvinian"; + case ("twi"): return "Twi"; + case ("uga"): return "Ugaritic"; + case ("uig"): return "Uighur"; + case ("ukr"): return "Ukrainian"; + case ("umb"): return "Umbundu"; + case ("und"): return "Undetermined"; + case ("urd"): return "Urdu"; + case ("uzb"): return "Uzbek"; + case ("vai"): return "Vai"; + case ("ven"): return "Venda"; + case ("vie"): return "Vietnamese"; + case ("vol"): return "Volapük"; + case ("vot"): return "Votic"; + case ("wak"): return "Wakashan languages"; + case ("wal"): return "Walamo"; + case ("war"): return "Waray"; + case ("was"): return "Washo"; + case ("cym"): return "Welsh"; + case ("wel"): return "Welsh"; + case ("wol"): return "Wolof"; + case ("xho"): return "Xhosa"; + case ("sah"): return "Yakut"; + case ("yao"): return "Yao"; + case ("yap"): return "Yap"; + case ("yid"): return "Yiddish"; + case ("yor"): return "Yoruba"; + case ("zap"): return "Zapotec"; + case ("zen"): return "Zenaga"; + case ("zha"): return "Zhuang"; + case ("zul"): return "Zulu"; + case ("zun"): return "Zuni"; + } + return "Any"; + } + } + } +} diff --git a/libse/ContainerFormats/Mp4/Boxes/Mdia.cs b/libse/ContainerFormats/Mp4/Boxes/Mdia.cs new file mode 100644 index 000000000..88e1d8a5d --- /dev/null +++ b/libse/ContainerFormats/Mp4/Boxes/Mdia.cs @@ -0,0 +1,70 @@ +using System; +using System.IO; + +namespace Nikse.SubtitleEdit.Core.ContainerFormats.Mp4.Boxes +{ + public class Mdia : Box + { + public Mdhd Mdhd; + public Minf Minf; + public readonly string HandlerType = null; + public readonly string HandlerName = string.Empty; + + public bool IsTextSubtitle + { + get { return HandlerType == "sbtl" || HandlerType == "text"; } + } + + public bool IsVobSubSubtitle + { + get { return HandlerType == "subp"; } + } + + public bool IsClosedCaption + { + get { return HandlerType == "clcp"; } + } + + public bool IsVideo + { + get { return HandlerType == "vide"; } + } + + public bool IsAudio + { + get { return HandlerType == "soun"; } + } + + public Mdia(FileStream fs, ulong maximumLength) + { + Position = (ulong)fs.Position; + while (fs.Position < (long)maximumLength) + { + if (!InitializeSizeAndName(fs)) + return; + + if (Name == "minf" && IsTextSubtitle || IsVobSubSubtitle || IsClosedCaption || IsVideo) + { + UInt32 timeScale = 90000; + if (Mdhd != null) + timeScale = Mdhd.TimeScale; + Minf = new Minf(fs, Position, timeScale, HandlerType, this); + } + else if (Name == "hdlr") + { + Buffer = new byte[Size - 4]; + fs.Read(Buffer, 0, Buffer.Length); + HandlerType = GetString(8, 4); + if (Size > 25) + HandlerName = GetString(24, Buffer.Length - (24 + 5)); // TODO: How to find this? + } + else if (Name == "mdhd") + { + Mdhd = new Mdhd(fs, Size); + } + fs.Seek((long)Position, SeekOrigin.Begin); + } + } + + } +} diff --git a/libse/ContainerFormats/Mp4/Boxes/Minf.cs b/libse/ContainerFormats/Mp4/Boxes/Minf.cs new file mode 100644 index 000000000..e6f4db4d7 --- /dev/null +++ b/libse/ContainerFormats/Mp4/Boxes/Minf.cs @@ -0,0 +1,27 @@ +using System; +using System.IO; + +namespace Nikse.SubtitleEdit.Core.ContainerFormats.Mp4.Boxes +{ + public class Minf : Box + { + + public Stbl Stbl; + + public Minf(FileStream fs, ulong maximumLength, UInt32 timeScale, string handlerType, Mdia mdia) + { + Position = (ulong)fs.Position; + while (fs.Position < (long)maximumLength) + { + if (!InitializeSizeAndName(fs)) + return; + + if (Name == "stbl") + Stbl = new Stbl(fs, Position, timeScale, handlerType, mdia); + + fs.Seek((long)Position, SeekOrigin.Begin); + } + } + + } +} diff --git a/libse/ContainerFormats/Mp4/Boxes/Moov.cs b/libse/ContainerFormats/Mp4/Boxes/Moov.cs new file mode 100644 index 000000000..d1e84d061 --- /dev/null +++ b/libse/ContainerFormats/Mp4/Boxes/Moov.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.IO; + +namespace Nikse.SubtitleEdit.Core.ContainerFormats.Mp4.Boxes +{ + public class Moov : Box + { + public Mvhd Mvhd; + public List Tracks; + + public Moov(FileStream fs, ulong maximumLength) + { + Tracks = new List(); + Position = (ulong)fs.Position; + while (fs.Position < (long)maximumLength) + { + if (!InitializeSizeAndName(fs)) + return; + + if (Name == "trak") + Tracks.Add(new Trak(fs, Position)); + else if (Name == "mvhd") + Mvhd = new Mvhd(fs); + + fs.Seek((long)Position, SeekOrigin.Begin); + } + } + } +} diff --git a/libse/ContainerFormats/Mp4/Boxes/Mvhd.cs b/libse/ContainerFormats/Mp4/Boxes/Mvhd.cs new file mode 100644 index 000000000..da1e917da --- /dev/null +++ b/libse/ContainerFormats/Mp4/Boxes/Mvhd.cs @@ -0,0 +1,25 @@ +using System.IO; + +namespace Nikse.SubtitleEdit.Core.ContainerFormats.Mp4.Boxes +{ + public class Mvhd : Box + { + public readonly uint CreationTime; + public readonly uint ModificationTime; + public readonly uint Duration; + public readonly uint TimeScale; + + public Mvhd(FileStream fs) + { + Buffer = new byte[20]; + int bytesRead = fs.Read(Buffer, 0, Buffer.Length); + if (bytesRead < Buffer.Length) + return; + + CreationTime = GetUInt(4); + ModificationTime = GetUInt(8); + TimeScale = GetUInt(12); + Duration = GetUInt(16); + } + } +} diff --git a/libse/ContainerFormats/Mp4/Boxes/Stbl.cs b/libse/ContainerFormats/Mp4/Boxes/Stbl.cs new file mode 100644 index 000000000..4ee62cc79 --- /dev/null +++ b/libse/ContainerFormats/Mp4/Boxes/Stbl.cs @@ -0,0 +1,201 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Nikse.Core; +using Nikse.SubtitleEdit.Core.SubtitleFormats; +using Nikse.SubtitleEdit.Core.VobSub; + +namespace Nikse.SubtitleEdit.Core.ContainerFormats.Mp4.Boxes +{ + public class Stbl : Box + { + public List Texts = new List(); + public List SubPictures = new List(); + public List StartTimeCodes = new List(); + public List EndTimeCodes = new List(); + public ulong StszSampleCount = 0; + private Mdia _mdia; + + public Stbl(FileStream fs, ulong maximumLength, UInt32 timeScale, string handlerType, Mdia mdia) + { + _mdia = mdia; + Position = (ulong)fs.Position; + while (fs.Position < (long)maximumLength) + { + if (!InitializeSizeAndName(fs)) + return; + + if (Name == "stco") // 32-bit - chunk offset + { + Buffer = new byte[Size - 4]; + fs.Read(Buffer, 0, Buffer.Length); + int version = Buffer[0]; + uint totalEntries = GetUInt(4); + + uint lastOffset = 0; + for (int i = 0; i < totalEntries; i++) + { + uint offset = GetUInt(8 + i * 4); + if (lastOffset + 5 < offset) + ReadText(fs, offset, handlerType); + lastOffset = offset; + } + } + else if (Name == "co64") // 64-bit + { + Buffer = new byte[Size - 4]; + fs.Read(Buffer, 0, Buffer.Length); + int version = Buffer[0]; + uint totalEntries = GetUInt(4); + + ulong lastOffset = 0; + for (int i = 0; i < totalEntries; i++) + { + ulong offset = GetUInt64(8 + i * 8); + if (lastOffset + 8 < offset) + ReadText(fs, offset, handlerType); + lastOffset = offset; + } + } + else if (Name == "stsz") // sample sizes + { + Buffer = new byte[Size - 4]; + fs.Read(Buffer, 0, Buffer.Length); + int version = Buffer[0]; + uint uniformSizeOfEachSample = GetUInt(4); + uint numberOfSampleSizes = GetUInt(8); + StszSampleCount = numberOfSampleSizes; + for (int i = 0; i < numberOfSampleSizes; i++) + { + if (12 + i * 4 + 4 < Buffer.Length) + { + uint sampleSize = GetUInt(12 + i * 4); + } + } + } + else if (Name == "stts") // sample table time to sample map + { + //https://developer.apple.com/library/mac/#documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-SW1 + + Buffer = new byte[Size - 4]; + fs.Read(Buffer, 0, Buffer.Length); + int version = Buffer[0]; + uint numberOfSampleTimes = GetUInt(4); + double totalTime = 0; + if (_mdia.IsClosedCaption) + { + for (int i = 0; i < numberOfSampleTimes; i++) + { + uint sampleCount = GetUInt(8 + i * 8); + uint sampleDelta = GetUInt(12 + i * 8); + for (int j = 0; j < sampleCount; j++) + { + totalTime += sampleDelta / (double)timeScale; + if (StartTimeCodes.Count > 0) + EndTimeCodes[EndTimeCodes.Count - 1] = totalTime - 0.001; + StartTimeCodes.Add(totalTime); + EndTimeCodes.Add(totalTime + 2.5); + } + } + } + else + { + for (int i = 0; i < numberOfSampleTimes; i++) + { + uint sampleCount = GetUInt(8 + i * 8); + uint sampleDelta = GetUInt(12 + i * 8); + totalTime += sampleDelta / (double)timeScale; + if (StartTimeCodes.Count <= EndTimeCodes.Count) + StartTimeCodes.Add(totalTime); + else + EndTimeCodes.Add(totalTime); + } + } + } + else if (Name == "stsc") // sample table sample to chunk map + { + Buffer = new byte[Size - 4]; + fs.Read(Buffer, 0, Buffer.Length); + int version = Buffer[0]; + uint numberOfSampleTimes = GetUInt(4); + for (int i = 0; i < numberOfSampleTimes; i++) + { + if (16 + i * 12 + 4 < Buffer.Length) + { + uint firstChunk = GetUInt(8 + i * 12); + uint samplesPerChunk = GetUInt(12 + i * 12); + uint sampleDescriptionIndex = GetUInt(16 + i * 12); + } + } + } + + fs.Seek((long)Position, SeekOrigin.Begin); + } + } + + private void ReadText(FileStream fs, ulong offset, string handlerType) + { + fs.Seek((long)offset, SeekOrigin.Begin); + var data = new byte[4]; + fs.Read(data, 0, 2); + uint textSize = (uint)GetWord(data, 0); + + if (handlerType == "subp") // VobSub created with Mp4Box + { + if (textSize > 100) + { + fs.Seek((long)offset, SeekOrigin.Begin); + data = new byte[textSize + 2]; + fs.Read(data, 0, data.Length); + SubPictures.Add(new SubPicture(data)); // TODO: Where is palette? + } + } + else + { + if (textSize == 0) + { + fs.Read(data, 2, 2); + textSize = GetUInt(data, 0); // don't get it exactly - seems like mp4box sometimes uses 2 bytes length field (first text record only)... handbrake uses 4 bytes + } + if (textSize > 0 && textSize < 500) + { + data = new byte[textSize]; + fs.Read(data, 0, data.Length); + string text = GetString(data, 0, (int)textSize).TrimEnd(); + + if (_mdia.IsClosedCaption) + { + var sb = new StringBuilder(); + for (int j = 8; j < data.Length - 3; j++) + { + string h = data[j].ToString("X2").ToLower(); + if (h.Length < 2) + h = "0" + h; + sb.Append(h); + if (j % 2 == 1) + sb.Append(' '); + } + string hex = sb.ToString(); + int errorCount = 0; + text = ScenaristClosedCaptions.GetSccText(hex, ref errorCount); + if (text.StartsWith('n') && text.Length > 1) + text = "" + text.Substring(1) + ""; + if (text.StartsWith("-n")) + text = text.Remove(0, 2); + if (text.StartsWith("-N")) + text = text.Remove(0, 2); + if (text.StartsWith('-') && !text.Contains(Environment.NewLine + "-")) + text = text.Remove(0, 1); + } + Texts.Add(text.Replace(Environment.NewLine, "\n").Replace("\n", Environment.NewLine)); + } + else + { + Texts.Add(string.Empty); + } + } + } + + } +} \ No newline at end of file diff --git a/libse/ContainerFormats/Mp4/Boxes/Tkhd.cs b/libse/ContainerFormats/Mp4/Boxes/Tkhd.cs new file mode 100644 index 000000000..ac9bf48d5 --- /dev/null +++ b/libse/ContainerFormats/Mp4/Boxes/Tkhd.cs @@ -0,0 +1,41 @@ +using System.IO; + +namespace Nikse.SubtitleEdit.Core.ContainerFormats.Mp4.Boxes +{ + public class Tkhd : Box + { + public readonly uint TrackId; + public readonly ulong Duration; + public readonly uint Width; + public readonly uint Height; + + public Tkhd(FileStream fs) + { + Buffer = new byte[84]; + int bytesRead = fs.Read(Buffer, 0, Buffer.Length); + if (bytesRead < Buffer.Length) + return; + + int version = Buffer[0]; + int addToIndex64Bit = 0; + if (version == 1) + addToIndex64Bit = 8; + + TrackId = GetUInt(12 + addToIndex64Bit); + if (version == 1) + { + Duration = GetUInt64(20 + addToIndex64Bit); + addToIndex64Bit += 4; + } + else + { + Duration = GetUInt(20 + addToIndex64Bit); + } + + Width = (uint)GetWord(76 + addToIndex64Bit); // skip decimals + Height = (uint)GetWord(80 + addToIndex64Bit); // skip decimals + //System.Windows.Forms.MessageBox.Show("Width: " + GetWord(76 + addToIndex64Bit).ToString() + "." + GetWord(78 + addToIndex64Bit).ToString()); + //System.Windows.Forms.MessageBox.Show("Height: " + GetWord(80 + addToIndex64Bit).ToString() + "." + GetWord(82 + addToIndex64Bit).ToString()); + } + } +} diff --git a/libse/ContainerFormats/Mp4/Boxes/Trak.cs b/libse/ContainerFormats/Mp4/Boxes/Trak.cs new file mode 100644 index 000000000..a4e8f294c --- /dev/null +++ b/libse/ContainerFormats/Mp4/Boxes/Trak.cs @@ -0,0 +1,29 @@ +using System.IO; + +namespace Nikse.SubtitleEdit.Core.ContainerFormats.Mp4.Boxes +{ + public class Trak : Box + { + + public Mdia Mdia; + public Tkhd Tkhd; + + public Trak(FileStream fs, ulong maximumLength) + { + Position = (ulong)fs.Position; + while (fs.Position < (long)maximumLength) + { + if (!InitializeSizeAndName(fs)) + return; + + if (Name == "mdia") + Mdia = new Mdia(fs, Position); + else if (Name == "tkhd") + Tkhd = new Tkhd(fs); + + fs.Seek((long)Position, SeekOrigin.Begin); + } + } + + } +} diff --git a/libse/ContainerFormats/Mp4/Mp4Parser.cs b/libse/ContainerFormats/Mp4/Mp4Parser.cs new file mode 100644 index 000000000..eae7ea73d --- /dev/null +++ b/libse/ContainerFormats/Mp4/Mp4Parser.cs @@ -0,0 +1,168 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Nikse.SubtitleEdit.Core.ContainerFormats.Mp4.Boxes; + +namespace Nikse.SubtitleEdit.Core.ContainerFormats.Mp4 +{ + /// + /// http://wiki.multimedia.cx/index.php?title=QuickTime_container + /// + public class MP4Parser : Box + { + public string FileName { get; private set; } + public Moov Moov { get; private set; } + + public List GetSubtitleTracks() + { + var list = new List(); + if (Moov != null && Moov.Tracks != null) + { + foreach (var trak in Moov.Tracks) + { + if (trak.Mdia != null && (trak.Mdia.IsTextSubtitle || trak.Mdia.IsVobSubSubtitle || trak.Mdia.IsClosedCaption) && trak.Mdia.Minf != null && trak.Mdia.Minf.Stbl != null) + { + list.Add(trak); + } + } + } + return list; + } + + public List GetAudioTracks() + { + var list = new List(); + if (Moov != null && Moov.Tracks != null) + { + foreach (var trak in Moov.Tracks) + { + if (trak.Mdia != null && trak.Mdia.IsAudio) + { + list.Add(trak); + } + } + } + return list; + } + + public List GetVideoTracks() + { + var list = new List(); + if (Moov != null && Moov.Tracks != null) + { + foreach (var trak in Moov.Tracks) + { + if (trak.Mdia != null && trak.Mdia.IsVideo) + { + list.Add(trak); + } + } + } + return list; + } + + public TimeSpan Duration + { + get + { + if (Moov != null && Moov.Mvhd != null && Moov.Mvhd.TimeScale > 0) + return TimeSpan.FromSeconds((double)Moov.Mvhd.Duration / Moov.Mvhd.TimeScale); + return new TimeSpan(); + } + } + + public DateTime CreationDate + { + get + { + if (Moov != null && Moov.Mvhd != null && Moov.Mvhd.TimeScale > 0) + return new DateTime(1904, 1, 1, 0, 0, 0, DateTimeKind.Utc).Add(TimeSpan.FromSeconds(Moov.Mvhd.CreationTime)); + return DateTime.Now; + } + } + + /// + /// Resolution of first video track. If not present returns 0.0 + /// + public System.Drawing.Point VideoResolution + { + get + { + if (Moov != null && Moov.Tracks != null) + { + foreach (var trak in Moov.Tracks) + { + if (trak != null && trak.Mdia != null && trak.Tkhd != null) + { + if (trak.Mdia.IsVideo) + return new System.Drawing.Point((int)trak.Tkhd.Width, (int)trak.Tkhd.Height); + } + } + } + return new System.Drawing.Point(0, 0); + } + } + + public MP4Parser(string fileName) + { + FileName = fileName; + using (var fs = new FileStream(FileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + ParseMp4(fs); + fs.Close(); + } + } + + public MP4Parser(FileStream fs) + { + FileName = null; + ParseMp4(fs); + } + + private void ParseMp4(FileStream fs) + { + int count = 0; + Position = 0; + fs.Seek(0, SeekOrigin.Begin); + bool moreBytes = true; + while (moreBytes) + { + moreBytes = InitializeSizeAndName(fs); + if (Size < 8) + return; + + if (Name == "moov" && Moov == null) + Moov = new Moov(fs, Position); // only scan first "moov" element + + count++; + if (count > 100) + break; + + if (Position > (ulong)fs.Length) + break; + fs.Seek((long)Position, SeekOrigin.Begin); + } + fs.Close(); + } + + internal double FrameRate + { + get + { + // Formula: moov.mdia.stbl.stsz.samplecount / (moov.trak.tkhd.duration / moov.mvhd.timescale) - http://www.w3.org/2008/WebVideo/Annotations/drafts/ontology10/CR/test.php?table=containerMPEG4 + if (Moov != null && Moov.Mvhd != null && Moov.Mvhd.TimeScale > 0) + { + var videoTracks = GetVideoTracks(); + if (videoTracks.Count > 0 && videoTracks[0].Tkhd != null && videoTracks[0].Mdia != null && videoTracks[0].Mdia.Minf != null && videoTracks[0].Mdia.Minf.Stbl != null) + { + double duration = videoTracks[0].Tkhd.Duration; + double sampleCount = videoTracks[0].Mdia.Minf.Stbl.StszSampleCount; + return sampleCount / (duration / Moov.Mvhd.TimeScale); + } + } + return 0; + } + } + + } +} diff --git a/libse/ContainerFormats/RiffDecodeHeader.cs b/libse/ContainerFormats/RiffDecodeHeader.cs new file mode 100644 index 000000000..8ff947586 --- /dev/null +++ b/libse/ContainerFormats/RiffDecodeHeader.cs @@ -0,0 +1,452 @@ +// (c) Giora Tamir (giora@gtamir.com), 2005 + +using System; +using System.Text; +using Nikse.Core; + +namespace Nikse.SubtitleEdit.Core.ContainerFormats +{ + public class RiffDecodeHeader + { + + #region private members + + private RiffParser m_parser; + + private double m_frameRate; + private int m_maxBitRate; + private int m_totalFrames; + private int m_numStreams; + private int m_width; + private int m_height; + + private string m_isft; + + private double m_vidDataRate; + private string m_vidHandler; + private double m_audDataRate; + private string m_audHandler; + + private int m_numChannels; + private int m_samplesPerSec; + private int m_bitsPerSec; + private int m_bitsPerSample; + + #endregion private members + + #region public members + + /// + /// Access the internal parser object + /// + public RiffParser Parser + { + get + { + return m_parser; + } + } + + public double FrameRate + { + get + { + double rate = 0.0; + if (m_frameRate > 0.0) + rate = 1000000.0 / m_frameRate; + return rate; + } + } + + public string MaxBitRate + { + get + { + return String.Format("{0:N} Kb/Sec", m_maxBitRate / 128); + } + } + + public int TotalFrames + { + get + { + return m_totalFrames; + } + } + + public double TotalMilliseconds + { + get + { + double totalTime = 0.0; + if (m_frameRate > 0.0) + { + totalTime = m_totalFrames * m_frameRate / TimeCode.BaseUnit; + } + return totalTime; + } + } + + public string NumStreams + { + get + { + return String.Format("Streams in file: {0:G}", m_numStreams); + } + } + + public string FrameSize + { + get + { + return String.Format("{0:G} x {1:G} pixels per frame", m_width, m_height); + } + } + + public int Width + { + get + { + return m_width; + } + } + + public int Height + { + get + { + return m_height; + } + } + + public string VideoDataRate + { + get + { + return String.Format("Video rate {0:N2} frames/Sec", m_vidDataRate); + } + } + + public string AudioDataRate + { + get + { + return String.Format("Audio rate {0:N2} Kb/Sec", m_audDataRate / TimeCode.BaseUnit); + } + } + + public string VideoHandler + { + get + { + return m_vidHandler; + } + } + + public string AudioHandler + { + get + { + return String.Format("Audio handler 4CC code: {0}", m_audHandler); + } + } + + public string ISFT + { + get + { + return m_isft; + } + } + + public string NumChannels + { + get + { + return String.Format("Audio channels: {0}", m_numChannels); + } + } + + public string SamplesPerSec + { + get + { + return String.Format("Audio rate: {0:N0} Samples/Sec", m_samplesPerSec); + } + } + + public string BitsPerSec + { + get + { + return String.Format("Audio rate: {0:N0} Bytes/Sec", m_bitsPerSec); + } + } + + public string BitsPerSample + { + get + { + return String.Format("Audio data: {0:N0} bits/Sample", m_bitsPerSample); + } + } + + #endregion public members + + #region Constructor + + public RiffDecodeHeader(RiffParser rp) + { + m_parser = rp; + } + + private void Clear() + { + m_frameRate = 0; + m_height = 0; + m_maxBitRate = 0; + m_numStreams = 0; + m_totalFrames = 0; + m_width = 0; + + m_isft = String.Empty; + + m_vidDataRate = 0; + m_audDataRate = 0; + m_vidHandler = String.Empty; + m_audHandler = String.Empty; + + m_numChannels = 0; + m_samplesPerSec = 0; + m_bitsPerSample = 0; + m_bitsPerSec = 0; + } + + #endregion Constructor + + #region Default element processing + + /// + /// Default list element handler - skip the entire list + /// + /// + /// + /// + private void ProcessList(RiffParser rp, int FourCC, int length) + { + rp.SkipData(length); + } + + #endregion Default element processing + + #region Decode AVI + + /// + /// Handle chunk elements found in the AVI file. Ignores unknown chunks and + /// + /// + /// + /// + /// + private void ProcessAVIChunk(RiffParser rp, int FourCC, int unpaddedLength, int paddedLength) + { + if (AviRiffData.ckidMainAVIHeader == FourCC) + { + // Main AVI header + DecodeAVIHeader(rp, paddedLength); + } + else if (AviRiffData.ckidAVIStreamHeader == FourCC) + { + // Stream header + DecodeAVIStream(rp, paddedLength); + } + else if (AviRiffData.ckidAVIISFT == FourCC) + { + Byte[] ba = new byte[paddedLength]; + rp.ReadData(ba, 0, paddedLength); + StringBuilder sb = new StringBuilder(unpaddedLength); + for (int i = 0; i < unpaddedLength; ++i) + { + if (0 != ba[i]) sb.Append((char)ba[i]); + } + + m_isft = sb.ToString(); + } + else + { + // Unknon chunk - skip + rp.SkipData(paddedLength); + } + } + + /// + /// Handle List elements found in the AVI file. Ignores unknown lists and recursively looks + /// at the content of known lists. + /// + /// + /// + /// + private void ProcessAVIList(RiffParser rp, int FourCC, int length) + { + RiffParser.ProcessChunkElement pac = ProcessAVIChunk; + RiffParser.ProcessListElement pal = ProcessAVIList; + + // Is this the header? + if ((AviRiffData.ckidAVIHeaderList == FourCC) + || (AviRiffData.ckidAVIStreamList == FourCC) + || (AviRiffData.ckidINFOList == FourCC)) + { + while (length > 0) + { + if (false == rp.ReadElement(ref length, pac, pal)) break; + } + } + else + { + // Unknown lists - ignore + rp.SkipData(length); + } + } + + public void ProcessMainAVI() + { + Clear(); + int length = Parser.DataSize; + + RiffParser.ProcessChunkElement pdc = ProcessAVIChunk; + RiffParser.ProcessListElement pal = ProcessAVIList; + + while (length > 0) + { + if (false == Parser.ReadElement(ref length, pdc, pal)) break; + } + } + + private unsafe void DecodeAVIHeader(RiffParser rp, int length) + { + //if (length < sizeof(AVIMAINHEADER)) + //{ + // throw new RiffParserException(String.Format("Header size mismatch. Needed {0} but only have {1}", + // sizeof(AVIMAINHEADER), length)); + //} + + byte[] ba = new byte[length]; + + if (rp.ReadData(ba, 0, length) != length) + { + throw new RiffParserException("Problem reading AVI header."); + } + + fixed (Byte* bp = &ba[0]) + { + AVIMAINHEADER* avi = (AVIMAINHEADER*)bp; + m_frameRate = avi->dwMicroSecPerFrame; + m_height = avi->dwHeight; + m_maxBitRate = avi->dwMaxBytesPerSec; + m_numStreams = avi->dwStreams; + m_totalFrames = avi->dwTotalFrames; + m_width = avi->dwWidth; + } + } + + private unsafe void DecodeAVIStream(RiffParser rp, int length) + { + byte[] ba = new byte[length]; + + if (rp.ReadData(ba, 0, length) != length) + { + throw new RiffParserException("Problem reading AVI header."); + } + + fixed (Byte* bp = &ba[0]) + { + AVISTREAMHEADER* avi = (AVISTREAMHEADER*)bp; + + if (AviRiffData.streamtypeVIDEO == avi->fccType) + { + m_vidHandler = RiffParser.FromFourCC(avi->fccHandler); + if (avi->dwScale > 0) + { + m_vidDataRate = (double)avi->dwRate / (double)avi->dwScale; + } + else + { + m_vidDataRate = 0.0; + } + } + else if (AviRiffData.streamtypeAUDIO == avi->fccType) + { + if (AviRiffData.ckidMP3 == avi->fccHandler) + { + m_audHandler = "MP3"; + } + else + { + m_audHandler = RiffParser.FromFourCC(avi->fccHandler); + } + if (avi->dwScale > 0) + { + m_audDataRate = 8.0 * (double)avi->dwRate / (double)avi->dwScale; + if (avi->dwSampleSize > 0) + { + m_audDataRate /= (double)avi->dwSampleSize; + } + } + else + { + m_audDataRate = 0.0; + } + } + } + } + + #endregion Decode AVI + + #region WAVE processing + + private void ProcessWaveChunk(RiffParser rp, int FourCC, int unpaddedLength, int length) + { + // Is this a 'fmt' chunk? + if (AviRiffData.ckidWaveFMT == FourCC) + { + DecodeWave(rp, length); + } + else + { + rp.SkipData(length); + } + } + + private unsafe void DecodeWave(RiffParser rp, int length) + { + byte[] ba = new byte[length]; + rp.ReadData(ba, 0, length); + + fixed (byte* bp = &ba[0]) + { + WAVEFORMATEX* wave = (WAVEFORMATEX*)bp; + m_numChannels = wave->nChannels; + m_bitsPerSec = wave->nAvgBytesPerSec; + m_bitsPerSample = wave->wBitsPerSample; + m_samplesPerSec = wave->nSamplesPerSec; + } + } + + public void ProcessMainWAVE() + { + Clear(); + int length = Parser.DataSize; + + RiffParser.ProcessChunkElement pdc = ProcessWaveChunk; + RiffParser.ProcessListElement pal = ProcessList; + + while (length > 0) + { + if (false == Parser.ReadElement(ref length, pdc, pal)) break; + } + } + + #endregion WAVE processing + + } +} \ No newline at end of file diff --git a/libse/ContainerFormats/RiffParser.cs b/libse/ContainerFormats/RiffParser.cs new file mode 100644 index 000000000..000ef05fc --- /dev/null +++ b/libse/ContainerFormats/RiffParser.cs @@ -0,0 +1,492 @@ +// (c) Giora Tamir (giora@gtamir.com), 2005 + +using System; +using System.IO; +using System.Runtime.Serialization; + +namespace Nikse.SubtitleEdit.Core.ContainerFormats +{ + #region RiffParserException + + [Serializable] + public class RiffParserException : ApplicationException + { + public RiffParserException() + { + } + + public RiffParserException(string message) + : base(message) + { + } + + public RiffParserException(string message, Exception inner) + : base(message, inner) + { + } + + public RiffParserException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } + + #endregion RiffParserException + + public class RiffParser : IDisposable + { + #region CONSTANTS + + public const int DWORDSIZE = 4; + public const int TWODWORDSSIZE = 8; + public const string RIFF4CC = "RIFF"; + public const string RIFX4CC = "RIFX"; + public const string LIST4CC = "LIST"; + + // Known file types + public static readonly int ckidAVI = ToFourCC("AVI "); + public static readonly int ckidWAV = ToFourCC("WAVE"); + public static readonly int ckidRMID = ToFourCC("RMID"); + + #endregion CONSTANTS + + #region private members + + private string m_filename; + private string m_shortname; + private long m_filesize; + private int m_datasize; + private FileStream m_stream; + private int m_fileriff; + private int m_filetype; + + // For non-thread-safe memory optimization + private byte[] m_eightBytes = new byte[TWODWORDSSIZE]; + private byte[] m_fourBytes = new byte[DWORDSIZE]; + + #endregion private members + + #region Delegates + + /// + /// Method to be called when a list element is found + /// + /// + /// + public delegate void ProcessListElement(RiffParser rp, int FourCCType, int length); + + /// + /// Method to be called when a chunk element is found + /// + /// + /// + /// + public delegate void ProcessChunkElement(RiffParser rp, int FourCCType, int unpaddedLength, int paddedLength); + + #endregion Delegates + + #region public Members + + /// + /// RIFF data segment size + /// + public int DataSize + { + get + { + return m_datasize; + } + } + + /// + /// Current file name + /// + public string FileName + { + get + { + return m_filename; + } + } + + /// + /// Current short (name only) file name + /// + public string ShortName + { + get + { + return m_shortname; + } + } + + /// + /// Return the general file type (RIFF or RIFX); + /// + public int FileRiff + { + get + { + return m_fileriff; + } + } + + /// + /// Return the specific file type (AVI/WAV...) + /// + public int FileType + { + get + { + return m_filetype; + } + } + + #endregion public Members + + /// + /// Determine if the file is a valid RIFF file + /// + /// File to examine + /// True if file is a RIFF file + public void OpenFile(string filename) + { + // Sanity check + if (null != m_stream) + { + throw new RiffParserException("RIFF file already open " + FileName); + } + + bool errorOccured = false; + + // Opening a new file + try + { + FileInfo fi = new FileInfo(filename); + m_filename = fi.FullName; + m_shortname = fi.Name; + m_filesize = fi.Length; + fi = null; + + //Console.WriteLine(ShortName + " is a valid file."); + + // Read the RIFF header + m_stream = new FileStream(m_filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + int FourCC; + int datasize; + int fileType; + + ReadTwoInts(out FourCC, out datasize); + ReadOneInt(out fileType); + + m_fileriff = FourCC; + m_filetype = fileType; + + // Check for a valid RIFF header + string riff = FromFourCC(FourCC); + if (riff == RIFF4CC || riff == RIFX4CC) + { + // Good header. Check size + //Console.WriteLine(ShortName + " has a valid type \"" + riff + "\""); + //Console.WriteLine(ShortName + " has a specific type of \"" + FromFourCC(fileType) + "\""); + + m_datasize = datasize; + if (m_filesize >= m_datasize + TWODWORDSSIZE) + { + //Console.WriteLine(ShortName + " has a valid size"); + } + else + { + m_stream.Close(); m_stream = null; + throw new RiffParserException("Error. Truncated file " + FileName); + } + } + else + { + m_stream.Close(); + m_stream.Dispose(); + m_stream = null; + throw new RiffParserException("Error. Not a valid RIFF file " + FileName); + } + } + catch (RiffParserException) + { + errorOccured = true; + throw; + } + catch (Exception exception) + { + errorOccured = true; + throw new RiffParserException("Error. Problem reading file " + FileName, exception); + } + finally + { + if (errorOccured && (null != m_stream)) + { + m_stream.Close(); + m_stream.Dispose(); + m_stream = null; + } + } + } + + /// + /// Read the next RIFF element invoking the correct delegate. + /// Returns true if an element can be read + /// + /// Reference to number of bytes left in the current list + /// Method to invoke if a chunk is found + /// Method to invoke if a list is found + /// + public bool ReadElement(ref int bytesleft, ProcessChunkElement chunk, ProcessListElement list) + { + // Are we done? + if (TWODWORDSSIZE > bytesleft) + { + return false; + } + + //Console.WriteLine(m_stream.Position.ToString() + ", " + bytesleft.ToString()); + + // We have enough bytes, read + int FourCC; + int size; + + ReadTwoInts(out FourCC, out size); + + // Reduce bytes left + bytesleft -= TWODWORDSSIZE; + + // Do we have enough bytes? + if (bytesleft < size) + { + // Skip the bad data and throw an exception + SkipData(bytesleft); + bytesleft = 0; + throw new RiffParserException("Element size mismatch for element " + FromFourCC(FourCC) + + " need " + size + " but have only " + bytesleft); + } + + // Examine the element, is it a list or a chunk + string type = FromFourCC(FourCC); + if (type == LIST4CC) + { + // We have a list + ReadOneInt(out FourCC); + + if (null == list) + { + SkipData(size - 4); + } + else + { + // Invoke the list method + list(this, FourCC, size - 4); + } + + // Adjust size + bytesleft -= size; + } + else + { + // Calculated padded size - padded to WORD boundary + int paddedSize = size; + if (0 != (size & 1)) ++paddedSize; + + if (null == chunk) + { + SkipData(paddedSize); + } + else + { + chunk(this, FourCC, size, paddedSize); + } + + // Adjust size + bytesleft -= paddedSize; + } + + return true; + } + + #region Stream access + + /// + /// Non-thread-safe method to read two ints from the stream + /// + /// Output FourCC int + /// Output chunk/list size + public unsafe void ReadTwoInts(out int FourCC, out int size) + { + try + { + int readsize = m_stream.Read(m_eightBytes, 0, TWODWORDSSIZE); + + if (TWODWORDSSIZE != readsize) + { + throw new RiffParserException("Unable to read. Corrupt RIFF file " + FileName); + } + + fixed (byte* bp = &m_eightBytes[0]) + { + FourCC = *((int*)bp); + size = *((int*)(bp + DWORDSIZE)); + } + } + catch (Exception ex) + { + throw new RiffParserException("Problem accessing RIFF file " + FileName, ex); + } + } + + /// + /// Non-thread-safe read a single int from the stream + /// + /// Output int + public unsafe void ReadOneInt(out int FourCC) + { + try + { + int readsize = m_stream.Read(m_fourBytes, 0, DWORDSIZE); + + if (DWORDSIZE != readsize) + { + throw new RiffParserException("Unable to read. Corrupt RIFF file " + FileName); + } + + fixed (byte* bp = &m_fourBytes[0]) + { + FourCC = *((int*)bp); + } + } + catch (Exception ex) + { + throw new RiffParserException("Problem accessing RIFF file " + FileName, ex); + } + } + + /// + /// Skip the specified number of bytes + /// + /// Number of bytes to skip + public void SkipData(int skipBytes) + { + try + { + m_stream.Seek(skipBytes, SeekOrigin.Current); + } + catch (Exception ex) + { + throw new RiffParserException("Problem seeking in file " + FileName, ex); + } + } + + /// + /// Read the specified length into the byte array at the specified + /// offset in the array + /// + /// Array of bytes to read into + /// Offset in the array to start from + /// Number of bytes to read + /// Number of bytes actually read + public int ReadData(Byte[] data, int offset, int length) + { + try + { + return m_stream.Read(data, offset, length); + } + catch (Exception ex) + { + throw new RiffParserException("Problem reading data in file " + FileName, ex); + } + } + + /// + /// Close the RIFF file + /// + public void CloseFile() + { + if (null != m_stream) + { + m_stream.Close(); + m_stream = null; + } + } + + #endregion Stream access + + #region FourCC conversion methods + + public static string FromFourCC(int FourCC) + { + char[] chars = new char[4]; + chars[0] = (char)(FourCC & 0xFF); + chars[1] = (char)((FourCC >> 8) & 0xFF); + chars[2] = (char)((FourCC >> 16) & 0xFF); + chars[3] = (char)((FourCC >> 24) & 0xFF); + + return new string(chars); + } + + public static int ToFourCC(string FourCC) + { + if (FourCC.Length != 4) + { + throw new Exception("FourCC strings must be 4 characters long " + FourCC); + } + + int result = ((int)FourCC[3]) << 24 + | ((int)FourCC[2]) << 16 + | ((int)FourCC[1]) << 8 + | ((int)FourCC[0]); + + return result; + } + + public static int ToFourCC(char[] FourCC) + { + if (FourCC.Length != 4) + { + throw new Exception("FourCC char arrays must be 4 characters long " + new string(FourCC)); + } + + int result = ((int)FourCC[3]) << 24 + | ((int)FourCC[2]) << 16 + | ((int)FourCC[1]) << 8 + | ((int)FourCC[0]); + + return result; + } + + public static int ToFourCC(char c0, char c1, char c2, char c3) + { + int result = ((int)c3) << 24 + | ((int)c2) << 16 + | ((int)c1) << 8 + | ((int)c0); + + return result; + } + + #endregion FourCC conversion methods + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + if (m_stream != null) + { + m_stream.Dispose(); + m_stream = null; + } + } + } + + } +} \ No newline at end of file diff --git a/libse/DetectEncoding/EncodingTools.cs b/libse/DetectEncoding/EncodingTools.cs new file mode 100644 index 000000000..5fd1cb6df --- /dev/null +++ b/libse/DetectEncoding/EncodingTools.cs @@ -0,0 +1,474 @@ +// Ripped from http://www.codeproject.com/KB/recipes/DetectEncoding.aspx + +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; +using Nikse.SubtitleEdit.Core.DetectEncoding.Multilang; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding +{ + public static class EncodingTools + { + // this only contains ascii, default windows code page and unicode + private static int[] PreferredEncodingsForStream; + + // this contains all codepages, sorted by preference and byte usage + private static int[] PreferredEncodings; + + /// + /// Static constructor that fills the default preferred codepages + /// + static EncodingTools() + { + List streamEcodings = new List(); + List allEncodings = new List(); + List mimeEcodings = new List(); + + // asscii - most simple so put it in first place... + streamEcodings.Add(Encoding.ASCII.CodePage); + mimeEcodings.Add(Encoding.ASCII.CodePage); + allEncodings.Add(Encoding.ASCII.CodePage); + + // add default 2nd for all encodings + allEncodings.Add(Encoding.Default.CodePage); + // default is single byte? + if (Encoding.Default.IsSingleByte) + { + // put it in second place + streamEcodings.Add(Encoding.Default.CodePage); + mimeEcodings.Add(Encoding.Default.CodePage); + } + + // prefer JIS over JIS-SHIFT (JIS is detected better than JIS-SHIFT) + // this one does include cyrilic (strange but true) + allEncodings.Add(50220); + mimeEcodings.Add(50220); + + // always allow unicode flavours for streams (they all have a preamble) + streamEcodings.Add(Encoding.Unicode.CodePage); + foreach (EncodingInfo enc in Encoding.GetEncodings()) + { + if (!streamEcodings.Contains(enc.CodePage)) + { + Encoding encoding = Encoding.GetEncoding(enc.CodePage); + if (encoding.GetPreamble().Length > 0) + streamEcodings.Add(enc.CodePage); + } + } + + // stream is done here + PreferredEncodingsForStream = streamEcodings.ToArray(); + + // all singlebyte encodings + foreach (EncodingInfo enc in Encoding.GetEncodings()) + { + if (!enc.GetEncoding().IsSingleByte) + continue; + + if (!allEncodings.Contains(enc.CodePage)) + allEncodings.Add(enc.CodePage); + + // only add iso and IBM encodings to mime encodings + if (enc.CodePage <= 1258) + { + mimeEcodings.Add(enc.CodePage); + } + } + + // add the rest (multibyte) + foreach (EncodingInfo enc in Encoding.GetEncodings()) + { + if (!enc.GetEncoding().IsSingleByte) + { + if (!allEncodings.Contains(enc.CodePage)) + allEncodings.Add(enc.CodePage); + + // only add iso and IBM encodings to mime encodings + if (enc.CodePage <= 1258) + { + mimeEcodings.Add(enc.CodePage); + } + } + } + + // add unicodes + mimeEcodings.Add(Encoding.Unicode.CodePage); + + PreferredEncodings = mimeEcodings.ToArray(); + } + + /// + /// Gets the best Encoding for usage in mime encodings + /// + /// text to detect + /// the suggested encoding + public static Encoding GetMostEfficientEncoding(string input) + { + return GetMostEfficientEncoding(input, PreferredEncodings); + } + + /// + /// Gets the best ISO Encoding for usage in a stream + /// + /// text to detect + /// the suggested encoding + public static Encoding GetMostEfficientEncodingForStream(string input) + { + return GetMostEfficientEncoding(input, PreferredEncodingsForStream); + } + + /// + /// Gets the best fitting encoding from a list of possible encodings + /// + /// text to detect + /// an array of codepages + /// the suggested encoding + public static Encoding GetMostEfficientEncoding(string input, int[] preferredEncodings) + { + Encoding enc = DetectOutgoingEncoding(input, preferredEncodings, true); + // unicode.. hmmm... check for smallest encoding + if (enc.CodePage == Encoding.Unicode.CodePage) + { + int byteCount = Encoding.UTF7.GetByteCount(input); + enc = Encoding.UTF7; + int bestByteCount = byteCount; + + // utf8 smaller? + byteCount = Encoding.UTF8.GetByteCount(input); + if (byteCount < bestByteCount) + { + enc = Encoding.UTF8; + bestByteCount = byteCount; + } + + // unicode smaller? + byteCount = Encoding.Unicode.GetByteCount(input); + if (byteCount < bestByteCount) + { + enc = Encoding.Unicode; + bestByteCount = byteCount; + } + } + else + { + } + return enc; + } + + public static Encoding DetectOutgoingEncoding(string input) + { + return DetectOutgoingEncoding(input, PreferredEncodings, true); + } + + public static Encoding DetectOutgoingStreamEncoding(string input) + { + return DetectOutgoingEncoding(input, PreferredEncodingsForStream, true); + } + + public static Encoding[] DetectOutgoingEncodings(string input) + { + return DetectOutgoingEncodings(input, PreferredEncodings, true); + } + + public static Encoding[] DetectOutgoingStreamEncodings(string input) + { + return DetectOutgoingEncodings(input, PreferredEncodingsForStream, true); + } + + private static Encoding DetectOutgoingEncoding(string input, int[] preferredEncodings, bool preserveOrder) + { + if (input == null) + throw new ArgumentNullException("input"); + + // empty strings can always be encoded as ASCII + if (input.Length == 0) + return Encoding.ASCII; + + Encoding result = Encoding.ASCII; + + // get the IMultiLanguage3 interface + IMultiLanguage3 multilang3 = new CMultiLanguageClass(); + if (multilang3 == null) + throw new COMException("Failed to get IMultilang3"); + try + { + int[] resultCodePages = new int[preferredEncodings != null ? preferredEncodings.Length : Encoding.GetEncodings().Length]; + uint detectedCodepages = (uint)resultCodePages.Length; + ushort specialChar = (ushort)'?'; + + // get unmanaged arrays + IntPtr pPrefEncs = preferredEncodings == null ? IntPtr.Zero : Marshal.AllocCoTaskMem(sizeof(uint) * preferredEncodings.Length); + IntPtr pDetectedEncs = Marshal.AllocCoTaskMem(sizeof(uint) * resultCodePages.Length); + + try + { + if (preferredEncodings != null) + Marshal.Copy(preferredEncodings, 0, pPrefEncs, preferredEncodings.Length); + + Marshal.Copy(resultCodePages, 0, pDetectedEncs, resultCodePages.Length); + + MLCPF options = MLCPF.MLDETECTF_VALID_NLS; + if (preserveOrder) + options |= MLCPF.MLDETECTF_PRESERVE_ORDER; + + if (preferredEncodings != null) + options |= MLCPF.MLDETECTF_PREFERRED_ONLY; + + multilang3.DetectOutboundCodePage(options, + input, (uint)input.Length, + pPrefEncs, (uint)(preferredEncodings == null ? 0 : preferredEncodings.Length), + + pDetectedEncs, ref detectedCodepages, + ref specialChar); + + // get result + if (detectedCodepages > 0) + { + int[] theResult = new int[detectedCodepages]; + Marshal.Copy(pDetectedEncs, theResult, 0, theResult.Length); + result = Encoding.GetEncoding(theResult[0]); + } + } + finally + { + if (pPrefEncs != IntPtr.Zero) + Marshal.FreeCoTaskMem(pPrefEncs); + Marshal.FreeCoTaskMem(pDetectedEncs); + } + } + finally + { + Marshal.FinalReleaseComObject(multilang3); + } + return result; + } + + public static Encoding[] DetectOutgoingEncodings(string input, int[] preferredEncodings, bool preserveOrder) + { + if (input == null) + throw new ArgumentNullException("input"); + + // empty strings can always be encoded as ASCII + if (input.Length == 0) + return new Encoding[] { Encoding.ASCII }; + + List result = new List(); + + // get the IMultiLanguage3 interface + IMultiLanguage3 multilang3 = new CMultiLanguageClass(); + if (multilang3 == null) + throw new COMException("Failed to get IMultilang3"); + try + { + int[] resultCodePages = new int[preferredEncodings.Length]; + uint detectedCodepages = (uint)resultCodePages.Length; + ushort specialChar = (ushort)'?'; + + // get unmanaged arrays + IntPtr pPrefEncs = Marshal.AllocCoTaskMem(sizeof(uint) * preferredEncodings.Length); + IntPtr pDetectedEncs = Marshal.AllocCoTaskMem(sizeof(uint) * resultCodePages.Length); + + try + { + Marshal.Copy(preferredEncodings, 0, pPrefEncs, preferredEncodings.Length); + + Marshal.Copy(resultCodePages, 0, pDetectedEncs, resultCodePages.Length); + + MLCPF options = MLCPF.MLDETECTF_VALID_NLS | MLCPF.MLDETECTF_PREFERRED_ONLY; + if (preserveOrder) + options |= MLCPF.MLDETECTF_PRESERVE_ORDER; + + options |= MLCPF.MLDETECTF_PREFERRED_ONLY; + + // finally... call to DetectOutboundCodePage + multilang3.DetectOutboundCodePage(options, + input, (uint)input.Length, + pPrefEncs, (uint)preferredEncodings.Length, + pDetectedEncs, ref detectedCodepages, + ref specialChar); + + // get result + if (detectedCodepages > 0) + { + int[] theResult = new int[detectedCodepages]; + Marshal.Copy(pDetectedEncs, theResult, 0, theResult.Length); + + // get the encodings for the codepages + for (int i = 0; i < detectedCodepages; i++) + result.Add(Encoding.GetEncoding(theResult[i])); + } + } + finally + { + if (pPrefEncs != IntPtr.Zero) + Marshal.FreeCoTaskMem(pPrefEncs); + Marshal.FreeCoTaskMem(pDetectedEncs); + } + } + finally + { + Marshal.FinalReleaseComObject(multilang3); + } + // nothing found + return result.ToArray(); + } + + /// + /// Detect the most probable codepage from an byte array + /// + /// array containing the raw data + /// the detected encoding or the default encoding if the detection failed + public static Encoding DetectInputCodepage(byte[] input) + { + try + { + Encoding[] detected = DetectInputCodepages(input, 1); + + if (detected.Length > 0) + return detected[0]; + return Encoding.Default; + } + catch (COMException) + { + // return default codepage on error + return Encoding.Default; + } + } + + /// + /// Rerurns up to maxEncodings codpages that are assumed to be apropriate + /// + /// array containing the raw data + /// maxiumum number of encodings to detect + /// an array of Encoding with assumed encodings + public static Encoding[] DetectInputCodepages(byte[] input, int maxEncodings) + { + if (maxEncodings < 1) + throw new ArgumentOutOfRangeException("maxEncodings", "at least one encoding must be returned"); + + if (input == null) + throw new ArgumentNullException("input"); + + // empty strings can always be encoded as ASCII + if (input.Length == 0) + return new Encoding[] { Encoding.ASCII }; + + // expand the string to be at least 256 bytes + if (input.Length < 256) + { + byte[] newInput = new byte[256]; + int steps = 256 / input.Length; + for (int i = 0; i < steps; i++) + Array.Copy(input, 0, newInput, input.Length * i, input.Length); + + int rest = 256 % input.Length; + if (rest > 0) + Array.Copy(input, 0, newInput, steps * input.Length, rest); + input = newInput; + } + + List result = new List(); + + // get the IMultiLanguage" interface + IMultiLanguage2 multilang2 = new CMultiLanguageClass(); + if (multilang2 == null) + throw new COMException("Failed to get IMultilang2"); + try + { + DetectEncodingInfo[] detectedEncdings = new DetectEncodingInfo[maxEncodings]; + + int scores = detectedEncdings.Length; + int srcLen = input.Length; + + // setup options (none) + const MLDETECTCP options = MLDETECTCP.MLDETECTCP_NONE; + + // finally... call to DetectInputCodepage + multilang2.DetectInputCodepage(options, 0, + ref input[0], ref srcLen, ref detectedEncdings[0], ref scores); + + // get result + if (scores > 0) + { + for (int i = 0; i < scores; i++) + { + // add the result + result.Add(Encoding.GetEncoding((int)detectedEncdings[i].nCodePage)); + } + } + } + finally + { + Marshal.FinalReleaseComObject(multilang2); + } + // nothing found + return result.ToArray(); + } + + /// + /// Opens a text file and returns the content + /// encoded in the most probable encoding + /// + /// path to the souce file + /// the text content of the file + public static string ReadTextFile(string path) + { + if (path == null) + throw new ArgumentNullException("path"); + + using (Stream fs = File.Open(path, FileMode.Open)) + { + byte[] rawData = new byte[fs.Length]; + Encoding enc = DetectInputCodepage(rawData); + return enc.GetString(rawData); + } + } + + /// + /// Returns a stream reader for the given + /// text file with the best encoding applied + /// + /// path to the file + /// a StreamReader for the file + public static StreamReader OpenTextFile(string path) + { + if (path == null) + throw new ArgumentNullException("path"); + return OpenTextStream(File.Open(path, FileMode.Open)); + } + + /// + /// Creates a stream reader from a stream and detects + /// the encoding form the first bytes in the stream + /// + /// a stream to wrap + /// the newly created StreamReader + public static StreamReader OpenTextStream(Stream stream) + { + // check stream parameter + if (stream == null) + throw new ArgumentNullException("stream"); + if (!stream.CanSeek) + throw new ArgumentException("the stream must support seek operations", "stream"); + + // assume default encoding at first place + Encoding detectedEncoding = Encoding.Default; + + // seek to stream start + stream.Seek(0, SeekOrigin.Begin); + + // buffer for preamble and up to 512b sample text for dection + byte[] buf = new byte[System.Math.Min(stream.Length, 512)]; + + stream.Read(buf, 0, buf.Length); + detectedEncoding = DetectInputCodepage(buf); + // seek back to stream start + stream.Seek(0, SeekOrigin.Begin); + + return new StreamReader(stream, detectedEncoding); + } + + } +} diff --git a/libse/DetectEncoding/Multilang/CMLangConvertCharset.cs b/libse/DetectEncoding/Multilang/CMLangConvertCharset.cs new file mode 100644 index 000000000..af6e6b913 --- /dev/null +++ b/libse/DetectEncoding/Multilang/CMLangConvertCharset.cs @@ -0,0 +1,9 @@ +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [ComImport, Guid("D66D6F98-CDAA-11D0-B822-00C04FC9B31F"), CoClass(typeof(CMLangConvertCharsetClass))] + public interface ICMLangConvertCharset : IMLangConvertCharset + { + } +} diff --git a/libse/DetectEncoding/Multilang/CMLangConvertCharsetClass.cs b/libse/DetectEncoding/Multilang/CMLangConvertCharsetClass.cs new file mode 100644 index 000000000..0ac202ac6 --- /dev/null +++ b/libse/DetectEncoding/Multilang/CMLangConvertCharsetClass.cs @@ -0,0 +1,24 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [ComImport, ClassInterface((short)0), TypeLibType((short)2), Guid("D66D6F99-CDAA-11D0-B822-00C04FC9B31F")] + public class CMLangConvertCharsetClass : ICMLangConvertCharset + { + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void DoConversion([In] ref byte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref byte pDstStr, [In, Out] ref uint pcDstSize); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void DoConversionFromUnicode([In] ref ushort pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref sbyte pDstStr, [In, Out] ref uint pcDstSize); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void DoConversionToUnicode([In] ref sbyte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref ushort pDstStr, [In, Out] ref uint pcDstSize); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void GetDestinationCodePage(out uint puiDstCodePage); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void GetProperty(out uint pdwProperty); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void GetSourceCodePage(out uint puiSrcCodePage); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void Initialize([In] uint uiSrcCodePage, [In] uint uiDstCodePage, [In] uint dwProperty); + } +} diff --git a/libse/DetectEncoding/Multilang/CMLangString.cs b/libse/DetectEncoding/Multilang/CMLangString.cs new file mode 100644 index 000000000..f45e42504 --- /dev/null +++ b/libse/DetectEncoding/Multilang/CMLangString.cs @@ -0,0 +1,9 @@ +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [ComImport, CoClass(typeof(CMLangStringClass)), Guid("C04D65CE-B70D-11D0-B188-00AA0038C969")] + public interface ICMLangString : IMLangString + { + } +} diff --git a/libse/DetectEncoding/Multilang/CMLangStringClass.cs b/libse/DetectEncoding/Multilang/CMLangStringClass.cs new file mode 100644 index 000000000..e02a40be3 --- /dev/null +++ b/libse/DetectEncoding/Multilang/CMLangStringClass.cs @@ -0,0 +1,67 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [ComImport, Guid("C04D65CF-B70D-11D0-B188-00AA0038C969"), ComConversionLoss, ClassInterface((short)0), TypeLibType((short)2)] + public class CMLangStringClass : ICMLangString, IMLangStringWStr, IMLangStringAStr + { + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void GetAStr([In] int lSrcPos, [In] int lSrcLen, [In] uint uCodePageIn, out uint puCodePageOut, out sbyte pszDest, [In] int cchDest, out int pcchActual, out int plActualLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern int GetLength(); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void GetLocale([In] int lSrcPos, [In] int lSrcMaxLen, out uint plocale, out int plLocalePos, out int plLocaleLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void GetMLStr([In] int lSrcPos, [In] int lSrcLen, [In, MarshalAs(UnmanagedType.IUnknown)] object pUnkOuter, [In] uint dwClsContext, [In] ref Guid piid, [MarshalAs(UnmanagedType.IUnknown)] out object ppDestMLStr, out int plDestPos, out int plDestLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void GetStrBufA([In] int lSrcPos, [In] int lSrcMaxLen, out uint puDestCodePage, [MarshalAs(UnmanagedType.Interface)] out IMLangStringBufA ppDestBuf, out int plDestLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void GetStrBufW([In] int lSrcPos, [In] int lSrcMaxLen, [MarshalAs(UnmanagedType.Interface)] out IMLangStringBufW ppDestBuf, out int plDestLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void GetWStr([In] int lSrcPos, [In] int lSrcLen, out ushort pszDest, [In] int cchDest, out int pcchActual, out int plActualLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern int IMLangStringAStrGetLength(); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMLangStringAStrGetLocale([In] int lSrcPos, [In] int lSrcMaxLen, out uint plocale, out int plLocalePos, out int plLocaleLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMLangStringAStrGetMLStr([In] int lSrcPos, [In] int lSrcLen, [In, MarshalAs(UnmanagedType.IUnknown)] object pUnkOuter, [In] uint dwClsContext, [In] ref Guid piid, [MarshalAs(UnmanagedType.IUnknown)] out object ppDestMLStr, out int plDestPos, out int plDestLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMLangStringAStrSetLocale([In] int lDestPos, [In] int lDestLen, [In] uint locale); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMLangStringAStrSetMLStr([In] int lDestPos, [In] int lDestLen, [In, MarshalAs(UnmanagedType.IUnknown)] object pSrcMLStr, [In] int lSrcPos, [In] int lSrcLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMLangStringAStrSync([In] int fNoAccess); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern int IMLangStringWStrGetLength(); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMLangStringWStrGetMLStr([In] int lSrcPos, [In] int lSrcLen, [In, MarshalAs(UnmanagedType.IUnknown)] object pUnkOuter, [In] uint dwClsContext, [In] ref Guid piid, [MarshalAs(UnmanagedType.IUnknown)] out object ppDestMLStr, out int plDestPos, out int plDestLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMLangStringWStrSetMLStr([In] int lDestPos, [In] int lDestLen, [In, MarshalAs(UnmanagedType.IUnknown)] object pSrcMLStr, [In] int lSrcPos, [In] int lSrcLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMLangStringWStrSync([In] int fNoAccess); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void LockAStr([In] int lSrcPos, [In] int lSrcLen, [In] int lFlags, [In] uint uCodePageIn, [In] int cchRequest, out uint puCodePageOut, [Out] IntPtr ppszDest, out int pcchDest, out int plDestLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void LockWStr([In] int lSrcPos, [In] int lSrcLen, [In] int lFlags, [In] int cchRequest, [Out] IntPtr ppszDest, out int pcchDest, out int plDestLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void SetAStr([In] int lDestPos, [In] int lDestLen, [In] uint uCodePage, [In] ref sbyte pszSrc, [In] int cchSrc, out int pcchActual, out int plActualLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void SetLocale([In] int lDestPos, [In] int lDestLen, [In] uint locale); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void SetMLStr([In] int lDestPos, [In] int lDestLen, [In, MarshalAs(UnmanagedType.IUnknown)] object pSrcMLStr, [In] int lSrcPos, [In] int lSrcLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void SetStrBufA([In] int lDestPos, [In] int lDestLen, [In] uint uCodePage, [In, MarshalAs(UnmanagedType.Interface)] IMLangStringBufA pSrcBuf, out int pcchActual, out int plActualLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void SetStrBufW([In] int lDestPos, [In] int lDestLen, [In, MarshalAs(UnmanagedType.Interface)] IMLangStringBufW pSrcBuf, out int pcchActual, out int plActualLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void SetWStr([In] int lDestPos, [In] int lDestLen, [In] ref ushort pszSrc, [In] int cchSrc, out int pcchActual, out int plActualLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void Sync([In] int fNoAccess); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void UnlockAStr([In] ref sbyte pszSrc, [In] int cchSrc, out int pcchActual, out int plActualLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void UnlockWStr([In] ref ushort pszSrc, [In] int cchSrc, out int pcchActual, out int plActualLen); + } +} diff --git a/libse/DetectEncoding/Multilang/CMultiLanguage.cs b/libse/DetectEncoding/Multilang/CMultiLanguage.cs new file mode 100644 index 000000000..258c42ee7 --- /dev/null +++ b/libse/DetectEncoding/Multilang/CMultiLanguage.cs @@ -0,0 +1,9 @@ +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [ComImport, Guid("275C23E1-3747-11D0-9FEA-00AA003F8646"), CoClass(typeof(CMultiLanguageClass))] + public interface ICMultiLanguage : IMultiLanguage + { + } +} diff --git a/libse/DetectEncoding/Multilang/CMultiLanguageClass.cs b/libse/DetectEncoding/Multilang/CMultiLanguageClass.cs new file mode 100644 index 000000000..f5316ccb2 --- /dev/null +++ b/libse/DetectEncoding/Multilang/CMultiLanguageClass.cs @@ -0,0 +1,217 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [ComImport, TypeLibType((short)2), ClassInterface((short)0), Guid("275C23E2-3747-11D0-9FEA-00AA003F8646")] + public class CMultiLanguageClass : ICMultiLanguage, IMLangFontLink, IMLangLineBreakConsole, IMLangFontLink2, IMultiLanguage3 + { + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void BreakLineA([In] uint locale, [In] uint uCodePage, [In] ref sbyte pszSrc, [In] int cchSrc, [In] int cMaxColumns, out int pcchLine, out int pcchSkip); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void BreakLineML([In, MarshalAs(UnmanagedType.Interface)] ICMLangString pSrcMLStr, [In] int lSrcPos, [In] int lSrcLen, [In] int cMinColumns, [In] int cMaxColumns, out int plLineLen, out int plSkipLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void BreakLineW([In] uint locale, [In] ref ushort pszSrc, [In] int cchSrc, [In] int cMaxColumns, out int pcchLine, out int pcchSkip); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void CodePagesToCodePage([In] uint dwCodePages, [In] uint uDefaultCodePage, out uint puCodePage); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void CodePageToCodePages([In] uint uCodePage, out uint pdwCodePages); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void CodePageToScriptID([In] uint uiCodePage, out byte pSid); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void ConvertString([In, Out] ref uint pdwMode, [In] uint dwSrcEncoding, [In] uint dwDstEncoding, [In] ref byte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref byte pDstStr, [In, Out] ref uint pcDstSize); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void ConvertStringFromUnicode([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref ushort pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref sbyte pDstStr, [In, Out] ref uint pcDstSize); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void ConvertStringFromUnicodeEx([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref ushort pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref sbyte pDstStr, [In, Out] ref uint pcDstSize, [In] uint dwFlag, [In] ref ushort lpFallBack); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void ConvertStringInIStream([In, Out] ref uint pdwMode, [In] uint dwFlag, [In] ref ushort lpFallBack, [In] uint dwSrcEncoding, [In] uint dwDstEncoding, [In, MarshalAs(UnmanagedType.Interface)] IStream pstmIn, [In, MarshalAs(UnmanagedType.Interface)] IStream pstmOut); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void ConvertStringReset(); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void ConvertStringToUnicode([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref sbyte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref ushort pDstStr, [In, Out] ref uint pcDstSize); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void ConvertStringToUnicodeEx([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref sbyte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref ushort pDstStr, [In, Out] ref uint pcDstSize, [In] uint dwFlag, [In] ref ushort lpFallBack); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void CreateConvertCharset([In] uint uiSrcCodePage, [In] uint uiDstCodePage, [In] uint dwProperty, [MarshalAs(UnmanagedType.Interface)] out ICMLangConvertCharset ppMLangConvertCharset); + + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void DetectCodepageInIStream([In] MLDETECTCP flags, + [In] uint dwPrefWinCodePage, + [In, MarshalAs(UnmanagedType.Interface)] IStream pstmIn, + [In, Out] ref DetectEncodingInfo lpEncoding, + [In, Out] ref int pnScores); + + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void DetectInputCodepage([In] MLDETECTCP flags, [In] uint dwPrefWinCodePage, + [In] ref byte pSrcStr, [In, Out] ref int pcSrcSize, + [In, Out] ref DetectEncodingInfo lpEncoding, + [In, Out] ref int pnScores); + + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void DetectOutboundCodePage([In] MLCPF dwFlags, [In, MarshalAs(UnmanagedType.LPWStr)] string lpWideCharStr, [In] uint cchWideChar, +[In] IntPtr puiPreferredCodePages, [In] uint nPreferredCodePages, [In] IntPtr puiDetectedCodePages, [In, Out] ref uint pnDetectedCodePages, [In] ref ushort lpSpecialChar); + + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void DetectOutboundCodePageInIStream([In] uint dwFlags, [In, MarshalAs(UnmanagedType.Interface)] IStream pStrIn, [In] ref uint puiPreferredCodePages, [In] uint nPreferredCodePages, [In] ref uint puiDetectedCodePages, [In, Out] ref uint pnDetectedCodePages, [In] ref ushort lpSpecialChar); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void EnumCodePages([In] uint grfFlags, [MarshalAs(UnmanagedType.Interface)] out IEnumCodePage ppEnumCodePage); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void EnumCodePages([In] uint grfFlags, [In] ushort langId, [MarshalAs(UnmanagedType.Interface)] out IEnumCodePage ppEnumCodePage); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void EnumRfc1766([MarshalAs(UnmanagedType.Interface)] out IEnumRfc1766 ppEnumRfc1766); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void EnumRfc1766([In] ushort langId, [MarshalAs(UnmanagedType.Interface)] out IEnumRfc1766 ppEnumRfc1766); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void EnumScripts([In] uint dwFlags, [In] ushort langId, [MarshalAs(UnmanagedType.Interface)] out IEnumScript ppEnumScript); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void GetCharCodePages([In] ushort chSrc, out uint pdwCodePages); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void GetCharsetInfo([In, MarshalAs(UnmanagedType.BStr)] string Charset, out tagMIMECSETINFO pCharsetInfo); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void GetCodePageDescription([In] uint uiCodePage, [In] uint lcid, [In, Out, MarshalAs(UnmanagedType.LPWStr)] string lpWideCharStr, [In] int cchWideChar); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void GetCodePageInfo([In] uint uiCodePage, out tagMIMECPINFO pCodePageInfo); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void GetCodePageInfo([In] uint uiCodePage, [In] ushort langId, out tagMIMECPINFO pCodePageInfo); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void GetFamilyCodePage([In] uint uiCodePage, out uint puiFamilyCodePage); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void GetFontCodePages([In, ComAliasName("MultiLanguage.wireHDC")] ref _RemotableHandle hDC, [In, ComAliasName("MultiLanguage.wireHFONT")] ref _RemotableHandle hFont, out uint pdwCodePages); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void GetFontUnicodeRanges([In, ComAliasName("MultiLanguage.wireHDC")] ref _RemotableHandle hDC, [In, Out] ref uint puiRanges, out tagUNICODERANGE pUranges); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void GetLcidFromRfc1766(out uint plocale, [In, MarshalAs(UnmanagedType.BStr)] string bstrRfc1766); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void GetNumberOfCodePageInfo(out uint pcCodePage); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void GetNumberOfScripts(out uint pnScripts); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void GetRfc1766FromLcid([In] uint locale, [MarshalAs(UnmanagedType.BStr)] out string pbstrRfc1766); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void GetRfc1766Info([In] uint locale, out tagRFC1766INFO pRfc1766Info); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void GetRfc1766Info([In] uint locale, [In] ushort langId, out tagRFC1766INFO pRfc1766Info); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void GetScriptFontInfo([In] byte sid, [In] uint dwFlags, [In, Out] ref uint puiFonts, out tagSCRIPFONTINFO pScriptFont); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void GetStrCodePages([In] ref ushort pszSrc, [In] int cchSrc, [In] uint dwPriorityCodePages, out uint pdwCodePages, out int pcchCodePages); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMLangFontLinkCodePagesToCodePage([In] uint dwCodePages, [In] uint uDefaultCodePage, out uint puCodePage); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMLangFontLinkCodePageToCodePages([In] uint uCodePage, out uint pdwCodePages); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMLangFontLinkGetCharCodePages([In] ushort chSrc, out uint pdwCodePages); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMLangFontLinkGetStrCodePages([In] ref ushort pszSrc, [In] int cchSrc, [In] uint dwPriorityCodePages, out uint pdwCodePages, out int pcchCodePages); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMLangFontLink2CodePagesToCodePage([In] uint dwCodePages, [In] uint uDefaultCodePage, out uint puCodePage); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMLangFontLink2CodePageToCodePages([In] uint uCodePage, out uint pdwCodePages); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMLangFontLink2GetCharCodePages([In] ushort chSrc, out uint pdwCodePages); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMLangFontLink2GetFontCodePages([In, ComAliasName("MultiLanguage.wireHDC")] ref _RemotableHandle hDC, [In, ComAliasName("MultiLanguage.wireHFONT")] ref _RemotableHandle hFont, out uint pdwCodePages); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMLangFontLink2GetStrCodePages([In] ref ushort pszSrc, [In] int cchSrc, [In] uint dwPriorityCodePages, out uint pdwCodePages, out int pcchCodePages); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMLangFontLink2ReleaseFont([In, ComAliasName("MultiLanguage.wireHFONT")] ref _RemotableHandle hFont); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMLangFontLink2ResetFontMapping(); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage2ConvertString([In, Out] ref uint pdwMode, [In] uint dwSrcEncoding, [In] uint dwDstEncoding, [In] ref byte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref byte pDstStr, [In, Out] ref uint pcDstSize); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage2ConvertStringFromUnicode([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref ushort pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref sbyte pDstStr, [In, Out] ref uint pcDstSize); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage2ConvertStringReset(); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage2ConvertStringToUnicode([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref sbyte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref ushort pDstStr, [In, Out] ref uint pcDstSize); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage2CreateConvertCharset([In] uint uiSrcCodePage, [In] uint uiDstCodePage, [In] uint dwProperty, [MarshalAs(UnmanagedType.Interface)] out ICMLangConvertCharset ppMLangConvertCharset); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage2GetCharsetInfo([In, MarshalAs(UnmanagedType.BStr)] string charset, out tagMIMECSETINFO pCharsetInfo); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage2GetFamilyCodePage([In] uint uiCodePage, out uint puiFamilyCodePage); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage2GetLcidFromRfc1766(out uint plocale, [In, MarshalAs(UnmanagedType.BStr)] string bstrRfc1766); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage2GetNumberOfCodePageInfo(out uint pcCodePage); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage2GetRfc1766FromLcid([In] uint locale, [MarshalAs(UnmanagedType.BStr)] out string pbstrRfc1766); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage2IsConvertible([In] uint dwSrcEncoding, [In] uint dwDstEncoding); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage3ConvertString([In, Out] ref uint pdwMode, [In] uint dwSrcEncoding, [In] uint dwDstEncoding, [In] ref byte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref byte pDstStr, [In, Out] ref uint pcDstSize); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage3ConvertStringFromUnicode([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref ushort pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref sbyte pDstStr, [In, Out] ref uint pcDstSize); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage3ConvertStringFromUnicodeEx([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref ushort pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref sbyte pDstStr, [In, Out] ref uint pcDstSize, [In] uint dwFlag, [In] ref ushort lpFallBack); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage3ConvertStringInIStream([In, Out] ref uint pdwMode, [In] uint dwFlag, [In] ref ushort lpFallBack, [In] uint dwSrcEncoding, [In] uint dwDstEncoding, [In, MarshalAs(UnmanagedType.Interface)] IStream pstmIn, [In, MarshalAs(UnmanagedType.Interface)] IStream pstmOut); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage3ConvertStringReset(); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage3ConvertStringToUnicode([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref sbyte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref ushort pDstStr, [In, Out] ref uint pcDstSize); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage3ConvertStringToUnicodeEx([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref sbyte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref ushort pDstStr, [In, Out] ref uint pcDstSize, [In] uint dwFlag, [In] ref ushort lpFallBack); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage3CreateConvertCharset([In] uint uiSrcCodePage, [In] uint uiDstCodePage, [In] uint dwProperty, [MarshalAs(UnmanagedType.Interface)] out ICMLangConvertCharset ppMLangConvertCharset); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage3DetectCodepageInIStream([In] uint dwFlag, [In] uint dwPrefWinCodePage, [In, MarshalAs(UnmanagedType.Interface)] IStream pstmIn, [In, Out] ref DetectEncodingInfo lpEncoding, [In, Out] ref int pnScores); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage3DetectInputCodepage([In] uint dwFlag, [In] uint dwPrefWinCodePage, [In] ref sbyte pSrcStr, [In, Out] ref int pcSrcSize, [In, Out] ref DetectEncodingInfo lpEncoding, [In, Out] ref int pnScores); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage3EnumCodePages([In] uint grfFlags, [In] ushort langId, [MarshalAs(UnmanagedType.Interface)] out IEnumCodePage ppEnumCodePage); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage3EnumRfc1766([In] ushort langId, [MarshalAs(UnmanagedType.Interface)] out IEnumRfc1766 ppEnumRfc1766); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage3EnumScripts([In] uint dwFlags, [In] ushort langId, [MarshalAs(UnmanagedType.Interface)] out IEnumScript ppEnumScript); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage3GetCharsetInfo([In, MarshalAs(UnmanagedType.BStr)] string charset, out tagMIMECSETINFO pCharsetInfo); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage3GetCodePageDescription([In] uint uiCodePage, [In] uint lcid, [In, Out, MarshalAs(UnmanagedType.LPWStr)] string lpWideCharStr, [In] int cchWideChar); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage3GetCodePageInfo([In] uint uiCodePage, [In] ushort langId, out tagMIMECPINFO pCodePageInfo); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage3GetFamilyCodePage([In] uint uiCodePage, out uint puiFamilyCodePage); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage3GetLcidFromRfc1766(out uint plocale, [In, MarshalAs(UnmanagedType.BStr)] string bstrRfc1766); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage3GetNumberOfCodePageInfo(out uint pcCodePage); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage3GetNumberOfScripts(out uint pnScripts); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage3GetRfc1766FromLcid([In] uint locale, [MarshalAs(UnmanagedType.BStr)] out string pbstrRfc1766); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage3GetRfc1766Info([In] uint locale, [In] ushort langId, out tagRFC1766INFO pRfc1766Info); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage3IsCodePageInstallable([In] uint uiCodePage); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage3IsConvertible([In] uint dwSrcEncoding, [In] uint dwDstEncoding); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage3SetMimeDBSource([In] tagMIMECONTF dwSource); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage3ValidateCodePage([In] uint uiCodePage, [In, ComAliasName("MultiLanguage.wireHWND")] ref _RemotableHandle hwnd); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IMultiLanguage3ValidateCodePageEx([In] uint uiCodePage, [In, ComAliasName("MultiLanguage.wireHWND")] ref _RemotableHandle hwnd, [In] uint dwfIODControl); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IsCodePageInstallable([In] uint uiCodePage); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void IsConvertible([In] uint dwSrcEncoding, [In] uint dwDstEncoding); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void MapFont([In, ComAliasName("MultiLanguage.wireHDC")] ref _RemotableHandle hDC, [In] uint dwCodePages, [In] ushort chSrc, [Out, ComAliasName("MultiLanguage.wireHFONT")] IntPtr pFont); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void MapFont([In, ComAliasName("MultiLanguage.wireHDC")] ref _RemotableHandle hDC, [In] uint dwCodePages, [In, ComAliasName("MultiLanguage.wireHFONT")] ref _RemotableHandle hSrcFont, [Out, ComAliasName("MultiLanguage.wireHFONT")] IntPtr phDestFont); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void ReleaseFont([In, ComAliasName("MultiLanguage.wireHFONT")] ref _RemotableHandle hFont); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void ResetFontMapping(); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void SetMimeDBSource([In] tagMIMECONTF dwSource); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void ValidateCodePage([In] uint uiCodePage, [In, ComAliasName("MultiLanguage.wireHWND")] ref _RemotableHandle hwnd); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public virtual extern void ValidateCodePageEx([In] uint uiCodePage, [In, ComAliasName("MultiLanguage.wireHWND")] ref _RemotableHandle hwnd, [In] uint dwfIODControl); + } +} diff --git a/libse/DetectEncoding/Multilang/IEnumCodePage.cs b/libse/DetectEncoding/Multilang/IEnumCodePage.cs new file mode 100644 index 000000000..dccb17608 --- /dev/null +++ b/libse/DetectEncoding/Multilang/IEnumCodePage.cs @@ -0,0 +1,18 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [ComImport, Guid("275C23E3-3747-11D0-9FEA-00AA003F8646"), InterfaceType((short)1)] + public interface IEnumCodePage + { + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void Clone([MarshalAs(UnmanagedType.Interface)] out IEnumCodePage ppEnum); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void Next([In] uint celt, out tagMIMECPINFO rgelt, out uint pceltFetched); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void Reset(); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void Skip([In] uint celt); + } +} diff --git a/libse/DetectEncoding/Multilang/IEnumRfc1766.cs b/libse/DetectEncoding/Multilang/IEnumRfc1766.cs new file mode 100644 index 000000000..22ef00378 --- /dev/null +++ b/libse/DetectEncoding/Multilang/IEnumRfc1766.cs @@ -0,0 +1,18 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [ComImport, Guid("3DC39D1D-C030-11D0-B81B-00C04FC9B31F"), InterfaceType((short)1)] + public interface IEnumRfc1766 + { + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void Clone([MarshalAs(UnmanagedType.Interface)] out IEnumRfc1766 ppEnum); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void Next([In] uint celt, out tagRFC1766INFO rgelt, out uint pceltFetched); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void Reset(); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void Skip([In] uint celt); + } +} diff --git a/libse/DetectEncoding/Multilang/IEnumScript.cs b/libse/DetectEncoding/Multilang/IEnumScript.cs new file mode 100644 index 000000000..0985bdd2a --- /dev/null +++ b/libse/DetectEncoding/Multilang/IEnumScript.cs @@ -0,0 +1,18 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [ComImport, Guid("AE5F1430-388B-11D2-8380-00C04F8F5DA1"), InterfaceType((short)1)] + public interface IEnumScript + { + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void Clone([MarshalAs(UnmanagedType.Interface)] out IEnumScript ppEnum); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void Next([In] uint celt, out tagSCRIPTINFO rgelt, out uint pceltFetched); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void Reset(); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void Skip([In] uint celt); + } +} diff --git a/libse/DetectEncoding/Multilang/IMLangCodePages.cs b/libse/DetectEncoding/Multilang/IMLangCodePages.cs new file mode 100644 index 000000000..519da4784 --- /dev/null +++ b/libse/DetectEncoding/Multilang/IMLangCodePages.cs @@ -0,0 +1,18 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [ComImport, Guid("359F3443-BD4A-11D0-B188-00AA0038C969"), InterfaceType((short)1)] + public interface IMLangCodePages + { + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetCharCodePages([In] ushort chSrc, out uint pdwCodePages); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetStrCodePages([In] ref ushort pszSrc, [In] int cchSrc, [In] uint dwPriorityCodePages, out uint pdwCodePages, out int pcchCodePages); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void CodePageToCodePages([In] uint uCodePage, out uint pdwCodePages); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void CodePagesToCodePage([In] uint dwCodePages, [In] uint uDefaultCodePage, out uint puCodePage); + } +} diff --git a/libse/DetectEncoding/Multilang/IMLangConvertCharset.cs b/libse/DetectEncoding/Multilang/IMLangConvertCharset.cs new file mode 100644 index 000000000..2155993d6 --- /dev/null +++ b/libse/DetectEncoding/Multilang/IMLangConvertCharset.cs @@ -0,0 +1,24 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [ComImport, Guid("D66D6F98-CDAA-11D0-B822-00C04FC9B31F"), InterfaceType((short)1)] + public interface IMLangConvertCharset + { + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void Initialize([In] uint uiSrcCodePage, [In] uint uiDstCodePage, [In] uint dwProperty); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetSourceCodePage(out uint puiSrcCodePage); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetDestinationCodePage(out uint puiDstCodePage); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetProperty(out uint pdwProperty); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void DoConversion([In] ref byte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref byte pDstStr, [In, Out] ref uint pcDstSize); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void DoConversionToUnicode([In] ref sbyte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref ushort pDstStr, [In, Out] ref uint pcDstSize); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void DoConversionFromUnicode([In] ref ushort pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref sbyte pDstStr, [In, Out] ref uint pcDstSize); + } +} diff --git a/libse/DetectEncoding/Multilang/IMLangFontLink.cs b/libse/DetectEncoding/Multilang/IMLangFontLink.cs new file mode 100644 index 000000000..6b161b2fc --- /dev/null +++ b/libse/DetectEncoding/Multilang/IMLangFontLink.cs @@ -0,0 +1,29 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +#pragma warning disable 0108 + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [ComImport, InterfaceType((short)1), ComConversionLoss, Guid("359F3441-BD4A-11D0-B188-00AA0038C969")] + public interface IMLangFontLink : IMLangCodePages + { + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetCharCodePages([In] ushort chSrc, out uint pdwCodePages); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetStrCodePages([In] ref ushort pszSrc, [In] int cchSrc, [In] uint dwPriorityCodePages, out uint pdwCodePages, out int pcchCodePages); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void CodePageToCodePages([In] uint uCodePage, out uint pdwCodePages); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void CodePagesToCodePage([In] uint dwCodePages, [In] uint uDefaultCodePage, out uint puCodePage); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetFontCodePages([In, ComAliasName("MultiLanguage.wireHDC")] ref _RemotableHandle hDC, [In, ComAliasName("MultiLanguage.wireHFONT")] ref _RemotableHandle hFont, out uint pdwCodePages); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void MapFont([In, ComAliasName("MultiLanguage.wireHDC")] ref _RemotableHandle hDC, [In] uint dwCodePages, [In, ComAliasName("MultiLanguage.wireHFONT")] ref _RemotableHandle hSrcFont, [Out, ComAliasName("MultiLanguage.wireHFONT")] IntPtr phDestFont); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void ReleaseFont([In, ComAliasName("MultiLanguage.wireHFONT")] ref _RemotableHandle hFont); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void ResetFontMapping(); + } +} diff --git a/libse/DetectEncoding/Multilang/IMLangFontLink2.cs b/libse/DetectEncoding/Multilang/IMLangFontLink2.cs new file mode 100644 index 000000000..93085c71f --- /dev/null +++ b/libse/DetectEncoding/Multilang/IMLangFontLink2.cs @@ -0,0 +1,37 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +#pragma warning disable 0108 + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [ComImport, ComConversionLoss, InterfaceType((short)1), Guid("DCCFC162-2B38-11D2-B7EC-00C04F8F5D9A")] + public interface IMLangFontLink2 : IMLangCodePages + { + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetCharCodePages([In] ushort chSrc, out uint pdwCodePages); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetStrCodePages([In] ref ushort pszSrc, [In] int cchSrc, [In] uint dwPriorityCodePages, out uint pdwCodePages, out int pcchCodePages); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void CodePageToCodePages([In] uint uCodePage, out uint pdwCodePages); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void CodePagesToCodePage([In] uint dwCodePages, [In] uint uDefaultCodePage, out uint puCodePage); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetFontCodePages([In, ComAliasName("MultiLanguage.wireHDC")] ref _RemotableHandle hDC, [In, ComAliasName("MultiLanguage.wireHFONT")] ref _RemotableHandle hFont, out uint pdwCodePages); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void ReleaseFont([In, ComAliasName("MultiLanguage.wireHFONT")] ref _RemotableHandle hFont); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void ResetFontMapping(); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void MapFont([In, ComAliasName("MultiLanguage.wireHDC")] ref _RemotableHandle hDC, [In] uint dwCodePages, [In] ushort chSrc, [Out, ComAliasName("MultiLanguage.wireHFONT")] IntPtr pFont); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetFontUnicodeRanges([In, ComAliasName("MultiLanguage.wireHDC")] ref _RemotableHandle hDC, [In, Out] ref uint puiRanges, out tagUNICODERANGE pUranges); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetScriptFontInfo([In] byte sid, [In] uint dwFlags, [In, Out] ref uint puiFonts, out tagSCRIPFONTINFO pScriptFont); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void CodePageToScriptID([In] uint uiCodePage, out byte pSid); + } +} + +#pragma warning restore 0108 diff --git a/libse/DetectEncoding/Multilang/IMLangLineBreakConsole.cs b/libse/DetectEncoding/Multilang/IMLangLineBreakConsole.cs new file mode 100644 index 000000000..254c0409b --- /dev/null +++ b/libse/DetectEncoding/Multilang/IMLangLineBreakConsole.cs @@ -0,0 +1,16 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [ComImport, InterfaceType((short)1), Guid("F5BE2EE1-BFD7-11D0-B188-00AA0038C969")] + public interface IMLangLineBreakConsole + { + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void BreakLineML([In, MarshalAs(UnmanagedType.Interface)] ICMLangString pSrcMLStr, [In] int lSrcPos, [In] int lSrcLen, [In] int cMinColumns, [In] int cMaxColumns, out int plLineLen, out int plSkipLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void BreakLineW([In] uint locale, [In] ref ushort pszSrc, [In] int cchSrc, [In] int cMaxColumns, out int pcchLine, out int pcchSkip); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void BreakLineA([In] uint locale, [In] uint uCodePage, [In] ref sbyte pszSrc, [In] int cchSrc, [In] int cMaxColumns, out int pcchLine, out int pcchSkip); + } +} diff --git a/libse/DetectEncoding/Multilang/IMLangString.cs b/libse/DetectEncoding/Multilang/IMLangString.cs new file mode 100644 index 000000000..0c11a7e75 --- /dev/null +++ b/libse/DetectEncoding/Multilang/IMLangString.cs @@ -0,0 +1,19 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [ComImport, Guid("C04D65CE-B70D-11D0-B188-00AA0038C969"), InterfaceType((short)1)] + public interface IMLangString + { + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void Sync([In] int fNoAccess); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + int GetLength(); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void SetMLStr([In] int lDestPos, [In] int lDestLen, [In, MarshalAs(UnmanagedType.IUnknown)] object pSrcMLStr, [In] int lSrcPos, [In] int lSrcLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetMLStr([In] int lSrcPos, [In] int lSrcLen, [In, MarshalAs(UnmanagedType.IUnknown)] object pUnkOuter, [In] uint dwClsContext, [In] ref Guid piid, [MarshalAs(UnmanagedType.IUnknown)] out object ppDestMLStr, out int plDestPos, out int plDestLen); + } +} diff --git a/libse/DetectEncoding/Multilang/IMLangStringAStr.cs b/libse/DetectEncoding/Multilang/IMLangStringAStr.cs new file mode 100644 index 000000000..7cbd3ab1f --- /dev/null +++ b/libse/DetectEncoding/Multilang/IMLangStringAStr.cs @@ -0,0 +1,39 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +#pragma warning disable 0108 + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [ComImport, Guid("C04D65D2-B70D-11D0-B188-00AA0038C969"), ComConversionLoss, InterfaceType((short)1)] + public interface IMLangStringAStr : IMLangString + { + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void Sync([In] int fNoAccess); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + int GetLength(); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void SetMLStr([In] int lDestPos, [In] int lDestLen, [In, MarshalAs(UnmanagedType.IUnknown)] object pSrcMLStr, [In] int lSrcPos, [In] int lSrcLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetMLStr([In] int lSrcPos, [In] int lSrcLen, [In, MarshalAs(UnmanagedType.IUnknown)] object pUnkOuter, [In] uint dwClsContext, [In] ref Guid piid, [MarshalAs(UnmanagedType.IUnknown)] out object ppDestMLStr, out int plDestPos, out int plDestLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void SetAStr([In] int lDestPos, [In] int lDestLen, [In] uint uCodePage, [In] ref sbyte pszSrc, [In] int cchSrc, out int pcchActual, out int plActualLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void SetStrBufA([In] int lDestPos, [In] int lDestLen, [In] uint uCodePage, [In, MarshalAs(UnmanagedType.Interface)] IMLangStringBufA pSrcBuf, out int pcchActual, out int plActualLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetAStr([In] int lSrcPos, [In] int lSrcLen, [In] uint uCodePageIn, out uint puCodePageOut, out sbyte pszDest, [In] int cchDest, out int pcchActual, out int plActualLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetStrBufA([In] int lSrcPos, [In] int lSrcMaxLen, out uint puDestCodePage, [MarshalAs(UnmanagedType.Interface)] out IMLangStringBufA ppDestBuf, out int plDestLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void LockAStr([In] int lSrcPos, [In] int lSrcLen, [In] int lFlags, [In] uint uCodePageIn, [In] int cchRequest, out uint puCodePageOut, [Out] IntPtr ppszDest, out int pcchDest, out int plDestLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void UnlockAStr([In] ref sbyte pszSrc, [In] int cchSrc, out int pcchActual, out int plActualLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void SetLocale([In] int lDestPos, [In] int lDestLen, [In] uint locale); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetLocale([In] int lSrcPos, [In] int lSrcMaxLen, out uint plocale, out int plLocalePos, out int plLocaleLen); + } +} + +#pragma warning restore 0108 diff --git a/libse/DetectEncoding/Multilang/IMLangStringBufA.cs b/libse/DetectEncoding/Multilang/IMLangStringBufA.cs new file mode 100644 index 000000000..e9fd55cf1 --- /dev/null +++ b/libse/DetectEncoding/Multilang/IMLangStringBufA.cs @@ -0,0 +1,21 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [ComImport, Guid("D24ACD23-BA72-11D0-B188-00AA0038C969"), InterfaceType((short)1), ComConversionLoss] + public interface IMLangStringBufA + { + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetStatus(out int plFlags, out int pcchBuf); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void LockBuf([In] int cchOffset, [In] int cchMaxLock, [Out] IntPtr ppszBuf, out int pcchBuf); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void UnlockBuf([In] ref sbyte pszBuf, [In] int cchOffset, [In] int cchWrite); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void Insert([In] int cchOffset, [In] int cchMaxInsert, out int pcchActual); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void Delete([In] int cchOffset, [In] int cchDelete); + } +} diff --git a/libse/DetectEncoding/Multilang/IMLangStringBufW.cs b/libse/DetectEncoding/Multilang/IMLangStringBufW.cs new file mode 100644 index 000000000..2b1937094 --- /dev/null +++ b/libse/DetectEncoding/Multilang/IMLangStringBufW.cs @@ -0,0 +1,21 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [ComImport, InterfaceType((short)1), Guid("D24ACD21-BA72-11D0-B188-00AA0038C969"), ComConversionLoss] + public interface IMLangStringBufW + { + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetStatus(out int plFlags, out int pcchBuf); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void LockBuf([In] int cchOffset, [In] int cchMaxLock, [Out] IntPtr ppszBuf, out int pcchBuf); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void UnlockBuf([In] ref ushort pszBuf, [In] int cchOffset, [In] int cchWrite); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void Insert([In] int cchOffset, [In] int cchMaxInsert, out int pcchActual); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void Delete([In] int cchOffset, [In] int cchDelete); + } +} diff --git a/libse/DetectEncoding/Multilang/IMLangStringWStr.cs b/libse/DetectEncoding/Multilang/IMLangStringWStr.cs new file mode 100644 index 000000000..899787597 --- /dev/null +++ b/libse/DetectEncoding/Multilang/IMLangStringWStr.cs @@ -0,0 +1,39 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +#pragma warning disable 0108 + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [ComImport, InterfaceType((short)1), ComConversionLoss, Guid("C04D65D0-B70D-11D0-B188-00AA0038C969")] + public interface IMLangStringWStr : IMLangString + { + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void Sync([In] int fNoAccess); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + int GetLength(); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void SetMLStr([In] int lDestPos, [In] int lDestLen, [In, MarshalAs(UnmanagedType.IUnknown)] object pSrcMLStr, [In] int lSrcPos, [In] int lSrcLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetMLStr([In] int lSrcPos, [In] int lSrcLen, [In, MarshalAs(UnmanagedType.IUnknown)] object pUnkOuter, [In] uint dwClsContext, [In] ref Guid piid, [MarshalAs(UnmanagedType.IUnknown)] out object ppDestMLStr, out int plDestPos, out int plDestLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void SetWStr([In] int lDestPos, [In] int lDestLen, [In] ref ushort pszSrc, [In] int cchSrc, out int pcchActual, out int plActualLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void SetStrBufW([In] int lDestPos, [In] int lDestLen, [In, MarshalAs(UnmanagedType.Interface)] IMLangStringBufW pSrcBuf, out int pcchActual, out int plActualLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetWStr([In] int lSrcPos, [In] int lSrcLen, out ushort pszDest, [In] int cchDest, out int pcchActual, out int plActualLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetStrBufW([In] int lSrcPos, [In] int lSrcMaxLen, [MarshalAs(UnmanagedType.Interface)] out IMLangStringBufW ppDestBuf, out int plDestLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void LockWStr([In] int lSrcPos, [In] int lSrcLen, [In] int lFlags, [In] int cchRequest, [Out] IntPtr ppszDest, out int pcchDest, out int plDestLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void UnlockWStr([In] ref ushort pszSrc, [In] int cchSrc, out int pcchActual, out int plActualLen); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void SetLocale([In] int lDestPos, [In] int lDestLen, [In] uint locale); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetLocale([In] int lSrcPos, [In] int lSrcMaxLen, out uint plocale, out int plLocalePos, out int plLocaleLen); + } +} + +#pragma warning restore 0108 diff --git a/libse/DetectEncoding/Multilang/IMultiLanguage.cs b/libse/DetectEncoding/Multilang/IMultiLanguage.cs new file mode 100644 index 000000000..c85ce2bdf --- /dev/null +++ b/libse/DetectEncoding/Multilang/IMultiLanguage.cs @@ -0,0 +1,40 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [ComImport, Guid("275C23E1-3747-11D0-9FEA-00AA003F8646"), InterfaceType((short)1)] + public interface IMultiLanguage + { + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetNumberOfCodePageInfo(out uint pcCodePage); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetCodePageInfo([In] uint uiCodePage, out tagMIMECPINFO pCodePageInfo); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetFamilyCodePage([In] uint uiCodePage, out uint puiFamilyCodePage); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void EnumCodePages([In] uint grfFlags, [MarshalAs(UnmanagedType.Interface)] out IEnumCodePage ppEnumCodePage); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetCharsetInfo([In, MarshalAs(UnmanagedType.BStr)] string charset, out tagMIMECSETINFO pCharsetInfo); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void IsConvertible([In] uint dwSrcEncoding, [In] uint dwDstEncoding); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void ConvertString([In, Out] ref uint pdwMode, [In] uint dwSrcEncoding, [In] uint dwDstEncoding, [In] ref byte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref byte pDstStr, [In, Out] ref uint pcDstSize); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void ConvertStringToUnicode([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref sbyte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref ushort pDstStr, [In, Out] ref uint pcDstSize); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void ConvertStringFromUnicode([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref ushort pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref sbyte pDstStr, [In, Out] ref uint pcDstSize); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void ConvertStringReset(); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetRfc1766FromLcid([In] uint locale, [MarshalAs(UnmanagedType.BStr)] out string pbstrRfc1766); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetLcidFromRfc1766(out uint plocale, [In, MarshalAs(UnmanagedType.BStr)] string bstrRfc1766); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void EnumRfc1766([MarshalAs(UnmanagedType.Interface)] out IEnumRfc1766 ppEnumRfc1766); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetRfc1766Info([In] uint locale, out tagRFC1766INFO pRfc1766Info); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void CreateConvertCharset([In] uint uiSrcCodePage, [In] uint uiDstCodePage, [In] uint dwProperty, [MarshalAs(UnmanagedType.Interface)] out ICMLangConvertCharset ppMLangConvertCharset); + } +} diff --git a/libse/DetectEncoding/Multilang/IMultiLanguage2.cs b/libse/DetectEncoding/Multilang/IMultiLanguage2.cs new file mode 100644 index 000000000..cec3321d2 --- /dev/null +++ b/libse/DetectEncoding/Multilang/IMultiLanguage2.cs @@ -0,0 +1,74 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [ComImport, InterfaceType((short)1), Guid("DCCFC164-2B38-11D2-B7EC-00C04F8F5D9A")] + public interface IMultiLanguage2 + { + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetNumberOfCodePageInfo(out uint pcCodePage); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetCodePageInfo([In] uint uiCodePage, [In] ushort langId, out tagMIMECPINFO pCodePageInfo); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetFamilyCodePage([In] uint uiCodePage, out uint puiFamilyCodePage); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void EnumCodePages([In] uint grfFlags, [In] ushort langId, [MarshalAs(UnmanagedType.Interface)] out IEnumCodePage ppEnumCodePage); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetCharsetInfo([In, MarshalAs(UnmanagedType.BStr)] string charset, out tagMIMECSETINFO pCharsetInfo); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void IsConvertible([In] uint dwSrcEncoding, [In] uint dwDstEncoding); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void ConvertString([In, Out] ref uint pdwMode, [In] uint dwSrcEncoding, [In] uint dwDstEncoding, [In] ref byte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref byte pDstStr, [In, Out] ref uint pcDstSize); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void ConvertStringToUnicode([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref sbyte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref ushort pDstStr, [In, Out] ref uint pcDstSize); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void ConvertStringFromUnicode([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref ushort pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref sbyte pDstStr, [In, Out] ref uint pcDstSize); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void ConvertStringReset(); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetRfc1766FromLcid([In] uint locale, [MarshalAs(UnmanagedType.BStr)] out string pbstrRfc1766); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetLcidFromRfc1766(out uint plocale, [In, MarshalAs(UnmanagedType.BStr)] string bstrRfc1766); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void EnumRfc1766([In] ushort langId, [MarshalAs(UnmanagedType.Interface)] out IEnumRfc1766 ppEnumRfc1766); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetRfc1766Info([In] uint locale, [In] ushort langId, out tagRFC1766INFO pRfc1766Info); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void CreateConvertCharset([In] uint uiSrcCodePage, [In] uint uiDstCodePage, [In] uint dwProperty, [MarshalAs(UnmanagedType.Interface)] out ICMLangConvertCharset ppMLangConvertCharset); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void ConvertStringInIStream([In, Out] ref uint pdwMode, [In] uint dwFlag, [In] ref ushort lpFallBack, [In] uint dwSrcEncoding, [In] uint dwDstEncoding, [In, MarshalAs(UnmanagedType.Interface)] IStream pstmIn, [In, MarshalAs(UnmanagedType.Interface)] IStream pstmOut); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void ConvertStringToUnicodeEx([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref sbyte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref ushort pDstStr, [In, Out] ref uint pcDstSize, [In] uint dwFlag, [In] ref ushort lpFallBack); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void ConvertStringFromUnicodeEx([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref ushort pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref sbyte pDstStr, [In, Out] ref uint pcDstSize, [In] uint dwFlag, [In] ref ushort lpFallBack); + + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void DetectCodepageInIStream([In] MLDETECTCP flags, + [In] uint dwPrefWinCodePage, + [In, MarshalAs(UnmanagedType.Interface)] IStream pstmIn, + [In, Out] ref DetectEncodingInfo lpEncoding, + [In, Out] ref int pnScores); + + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void DetectInputCodepage([In] MLDETECTCP flags, [In] uint dwPrefWinCodePage, + [In] ref byte pSrcStr, [In, Out] ref int pcSrcSize, + [In, Out] ref DetectEncodingInfo lpEncoding, + [In, Out] ref int pnScores); + + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void ValidateCodePage([In] uint uiCodePage, [In, ComAliasName("MultiLanguage.wireHWND")] ref _RemotableHandle hwnd); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetCodePageDescription([In] uint uiCodePage, [In] uint lcid, [In, Out, MarshalAs(UnmanagedType.LPWStr)] string lpWideCharStr, [In] int cchWideChar); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void IsCodePageInstallable([In] uint uiCodePage); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void SetMimeDBSource([In] tagMIMECONTF dwSource); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetNumberOfScripts(out uint pnScripts); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void EnumScripts([In] uint dwFlags, [In] ushort langId, [MarshalAs(UnmanagedType.Interface)] out IEnumScript ppEnumScript); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void ValidateCodePageEx([In] uint uiCodePage, [In, ComAliasName("MultiLanguage.wireHWND")] ref _RemotableHandle hwnd, [In] uint dwfIODControl); + } +} diff --git a/libse/DetectEncoding/Multilang/IMultiLanguage3.cs b/libse/DetectEncoding/Multilang/IMultiLanguage3.cs new file mode 100644 index 000000000..c455fce4f --- /dev/null +++ b/libse/DetectEncoding/Multilang/IMultiLanguage3.cs @@ -0,0 +1,92 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +#pragma warning disable 0108 + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [ComImport, InterfaceType((short)1), Guid("4E5868AB-B157-4623-9ACC-6A1D9CAEBE04")] + public interface IMultiLanguage3 : IMultiLanguage2 + { + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetNumberOfCodePageInfo(out uint pcCodePage); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetCodePageInfo([In] uint uiCodePage, [In] ushort langId, out tagMIMECPINFO pCodePageInfo); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetFamilyCodePage([In] uint uiCodePage, out uint puiFamilyCodePage); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void EnumCodePages([In] uint grfFlags, [In] ushort langId, [MarshalAs(UnmanagedType.Interface)] out IEnumCodePage ppEnumCodePage); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetCharsetInfo([In, MarshalAs(UnmanagedType.BStr)] string Charset, out tagMIMECSETINFO pCharsetInfo); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void IsConvertible([In] uint dwSrcEncoding, [In] uint dwDstEncoding); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void ConvertString([In, Out] ref uint pdwMode, [In] uint dwSrcEncoding, [In] uint dwDstEncoding, [In] ref byte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref byte pDstStr, [In, Out] ref uint pcDstSize); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void ConvertStringToUnicode([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref sbyte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref ushort pDstStr, [In, Out] ref uint pcDstSize); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void ConvertStringFromUnicode([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref ushort pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref sbyte pDstStr, [In, Out] ref uint pcDstSize); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void ConvertStringReset(); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetRfc1766FromLcid([In] uint locale, [MarshalAs(UnmanagedType.BStr)] out string pbstrRfc1766); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetLcidFromRfc1766(out uint plocale, [In, MarshalAs(UnmanagedType.BStr)] string bstrRfc1766); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void EnumRfc1766([In] ushort langId, [MarshalAs(UnmanagedType.Interface)] out IEnumRfc1766 ppEnumRfc1766); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetRfc1766Info([In] uint locale, [In] ushort langId, out tagRFC1766INFO pRfc1766Info); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void CreateConvertCharset([In] uint uiSrcCodePage, [In] uint uiDstCodePage, [In] uint dwProperty, [MarshalAs(UnmanagedType.Interface)] out ICMLangConvertCharset ppMLangConvertCharset); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void ConvertStringInIStream([In, Out] ref uint pdwMode, [In] uint dwFlag, [In] ref ushort lpFallBack, [In] uint dwSrcEncoding, [In] uint dwDstEncoding, [In, MarshalAs(UnmanagedType.Interface)] IStream pstmIn, [In, MarshalAs(UnmanagedType.Interface)] IStream pstmOut); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void ConvertStringToUnicodeEx([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref sbyte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref ushort pDstStr, [In, Out] ref uint pcDstSize, [In] uint dwFlag, [In] ref ushort lpFallBack); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void ConvertStringFromUnicodeEx([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref ushort pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref sbyte pDstStr, [In, Out] ref uint pcDstSize, [In] uint dwFlag, [In] ref ushort lpFallBack); + + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void DetectCodepageInIStream([In] MLDETECTCP flags, + [In] uint dwPrefWinCodePage, + [In, MarshalAs(UnmanagedType.Interface)] IStream pstmIn, + [In, Out] ref DetectEncodingInfo lpEncoding, + [In, Out] ref int pnScores); + + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void DetectInputCodepage([In] MLDETECTCP flags, [In] uint dwPrefWinCodePage, + [In] ref byte pSrcStr, [In, Out] ref int pcSrcSize, + [In, Out] ref DetectEncodingInfo lpEncoding, + [In, Out] ref int pnScores); + + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void ValidateCodePage([In] uint uiCodePage, [In, ComAliasName("MultiLanguage.wireHWND")] ref _RemotableHandle hwnd); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetCodePageDescription([In] uint uiCodePage, [In] uint lcid, [In, Out, MarshalAs(UnmanagedType.LPWStr)] string lpWideCharStr, [In] int cchWideChar); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void IsCodePageInstallable([In] uint uiCodePage); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void SetMimeDBSource([In] tagMIMECONTF dwSource); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void GetNumberOfScripts(out uint pnScripts); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void EnumScripts([In] uint dwFlags, [In] ushort langId, [MarshalAs(UnmanagedType.Interface)] out IEnumScript ppEnumScript); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void ValidateCodePageEx([In] uint uiCodePage, [In, ComAliasName("MultiLanguage.wireHWND")] ref _RemotableHandle hwnd, [In] uint dwfIODControl); + + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void DetectOutboundCodePage([In] MLCPF dwFlags, + [In, MarshalAs(UnmanagedType.LPWStr)] string lpWideCharStr, + [In] uint cchWideChar, + [In] IntPtr puiPreferredCodePages, + [In] uint nPreferredCodePages, + [In] IntPtr puiDetectedCodePages, + [In, Out] ref uint pnDetectedCodePages, + [In] ref ushort lpSpecialChar); + + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void DetectOutboundCodePageInIStream([In] uint dwFlags, [In, MarshalAs(UnmanagedType.Interface)] IStream pStrIn, [In] ref uint puiPreferredCodePages, [In] uint nPreferredCodePages, [In] ref uint puiDetectedCodePages, [In, Out] ref uint pnDetectedCodePages, [In] ref ushort lpSpecialChar); + } +} + +#pragma warning restore 0108 diff --git a/libse/DetectEncoding/Multilang/ISequentialStream.cs b/libse/DetectEncoding/Multilang/ISequentialStream.cs new file mode 100644 index 000000000..4abaa1444 --- /dev/null +++ b/libse/DetectEncoding/Multilang/ISequentialStream.cs @@ -0,0 +1,15 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [ComImport, Guid("0C733A30-2A1C-11CE-ADE5-00AA0044773D"), InterfaceType((short)1)] + public interface ISequentialStream + { + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void RemoteRead(IntPtr pv, uint cb, ref uint pcbRead); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void RemoteWrite([In] ref byte pv, [In] uint cb, ref uint pcbWritten); + } +} diff --git a/libse/DetectEncoding/Multilang/IStream.cs b/libse/DetectEncoding/Multilang/IStream.cs new file mode 100644 index 000000000..ac5b0aa1e --- /dev/null +++ b/libse/DetectEncoding/Multilang/IStream.cs @@ -0,0 +1,37 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +#pragma warning disable 0108 + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [ComImport, Guid("0000000C-0000-0000-C000-000000000046"), InterfaceType((short)1)] + public interface IStream : ISequentialStream + { + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void RemoteRead(IntPtr pv, uint cb, ref uint pcbRead); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void RemoteWrite([In] ref byte pv, [In] uint cb, ref uint pcbWritten); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void RemoteSeek([In] _LARGE_INTEGER dlibMove, [In] uint dwOrigin, IntPtr plibNewPosition); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void SetSize([In] _ULARGE_INTEGER libNewSize); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void RemoteCopyTo([In, MarshalAs(UnmanagedType.Interface)] IStream pstm, [In] _ULARGE_INTEGER cb, out _ULARGE_INTEGER pcbRead, out _ULARGE_INTEGER pcbWritten); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void Commit([In] uint grfCommitFlags); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void Revert(); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void LockRegion([In] _ULARGE_INTEGER libOffset, [In] _ULARGE_INTEGER cb, [In] uint dwLockType); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void UnlockRegion([In] _ULARGE_INTEGER libOffset, [In] _ULARGE_INTEGER cb, [In] uint dwLockType); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void Stat(out tagSTATSTG pstatstg, [In] uint grfStatFlag); + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + void Clone([MarshalAs(UnmanagedType.Interface)] out IStream ppstm); + } +} + +#pragma warning restore 0108 diff --git a/libse/DetectEncoding/Multilang/_FILETIME.cs b/libse/DetectEncoding/Multilang/_FILETIME.cs new file mode 100644 index 000000000..deb340a20 --- /dev/null +++ b/libse/DetectEncoding/Multilang/_FILETIME.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct _FILETIME + { + public uint dwLowDateTime; + public uint dwHighDateTime; + } +} diff --git a/libse/DetectEncoding/Multilang/_LARGE_INTEGER.cs b/libse/DetectEncoding/Multilang/_LARGE_INTEGER.cs new file mode 100644 index 000000000..1a838340e --- /dev/null +++ b/libse/DetectEncoding/Multilang/_LARGE_INTEGER.cs @@ -0,0 +1,10 @@ +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [StructLayout(LayoutKind.Sequential, Pack = 8)] + public struct _LARGE_INTEGER + { + public long QuadPart; + } +} diff --git a/libse/DetectEncoding/Multilang/_RemotableHandle.cs b/libse/DetectEncoding/Multilang/_RemotableHandle.cs new file mode 100644 index 000000000..11fedbced --- /dev/null +++ b/libse/DetectEncoding/Multilang/_RemotableHandle.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct _RemotableHandle + { + public int fContext; + public __MIDL_IWinTypes_0009 u; + } +} diff --git a/libse/DetectEncoding/Multilang/_ULARGE_INTEGER.cs b/libse/DetectEncoding/Multilang/_ULARGE_INTEGER.cs new file mode 100644 index 000000000..20d4998fb --- /dev/null +++ b/libse/DetectEncoding/Multilang/_ULARGE_INTEGER.cs @@ -0,0 +1,10 @@ +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [StructLayout(LayoutKind.Sequential, Pack = 8)] + public struct _ULARGE_INTEGER + { + public ulong QuadPart; + } +} diff --git a/libse/DetectEncoding/Multilang/__MIDL_IWinTypes_0009.cs b/libse/DetectEncoding/Multilang/__MIDL_IWinTypes_0009.cs new file mode 100644 index 000000000..df04d1cfc --- /dev/null +++ b/libse/DetectEncoding/Multilang/__MIDL_IWinTypes_0009.cs @@ -0,0 +1,13 @@ +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [StructLayout(LayoutKind.Explicit, Pack = 4)] + public struct __MIDL_IWinTypes_0009 + { + [FieldOffset(0)] + public int hInproc; + [FieldOffset(0)] + public int hRemote; + } +} diff --git a/libse/DetectEncoding/Multilang/tagDetectEncodingInfo.cs b/libse/DetectEncoding/Multilang/tagDetectEncodingInfo.cs new file mode 100644 index 000000000..d44c7dedd --- /dev/null +++ b/libse/DetectEncoding/Multilang/tagDetectEncodingInfo.cs @@ -0,0 +1,15 @@ +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + /// Thanks to jannewe for finding the fix! + /// http://www.codeproject.com/KB/recipes/DetectEncoding.aspx?msg=3247475#xx3247475xx + [StructLayout(LayoutKind.Sequential)] + public struct DetectEncodingInfo + { + public uint nLangID; + public uint nCodePage; + public int nDocPercent; + public int nConfidence; + } +} diff --git a/libse/DetectEncoding/Multilang/tagMIMECONTF.cs b/libse/DetectEncoding/Multilang/tagMIMECONTF.cs new file mode 100644 index 000000000..352c3b9b5 --- /dev/null +++ b/libse/DetectEncoding/Multilang/tagMIMECONTF.cs @@ -0,0 +1,20 @@ +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + + public enum tagMIMECONTF + { + MIMECONTF_BROWSER = 2, + MIMECONTF_EXPORT = 0x400, + MIMECONTF_IMPORT = 8, + MIMECONTF_MAILNEWS = 1, + MIMECONTF_MIME_IE4 = 0x10000000, + MIMECONTF_MIME_LATEST = 0x20000000, + MIMECONTF_MIME_REGISTRY = 0x40000000, + MIMECONTF_MINIMAL = 4, + MIMECONTF_PRIVCONVERTER = 0x10000, + MIMECONTF_SAVABLE_BROWSER = 0x200, + MIMECONTF_SAVABLE_MAILNEWS = 0x100, + MIMECONTF_VALID = 0x20000, + MIMECONTF_VALID_NLS = 0x40000 + } +} diff --git a/libse/DetectEncoding/Multilang/tagMIMECPINFO.cs b/libse/DetectEncoding/Multilang/tagMIMECPINFO.cs new file mode 100644 index 000000000..3b0d3553e --- /dev/null +++ b/libse/DetectEncoding/Multilang/tagMIMECPINFO.cs @@ -0,0 +1,25 @@ +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct tagMIMECPINFO + { + public uint dwFlags; + public uint uiCodePage; + public uint uiFamilyCodePage; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x40)] + public ushort[] wszDescription; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 50)] + public ushort[] wszWebCharset; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 50)] + public ushort[] wszHeaderCharset; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 50)] + public ushort[] wszBodyCharset; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x20)] + public ushort[] wszFixedWidthFont; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x20)] + public ushort[] wszProportionalFont; + public byte bGDICharset; + } +} diff --git a/libse/DetectEncoding/Multilang/tagMIMECSETINFO.cs b/libse/DetectEncoding/Multilang/tagMIMECSETINFO.cs new file mode 100644 index 000000000..352509cb4 --- /dev/null +++ b/libse/DetectEncoding/Multilang/tagMIMECSETINFO.cs @@ -0,0 +1,13 @@ +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct tagMIMECSETINFO + { + public uint uiCodePage; + public uint uiInternetEncoding; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 50)] + public ushort[] wszCharset; + } +} diff --git a/libse/DetectEncoding/Multilang/tagMLCPF.cs b/libse/DetectEncoding/Multilang/tagMLCPF.cs new file mode 100644 index 000000000..0bee4a62a --- /dev/null +++ b/libse/DetectEncoding/Multilang/tagMLCPF.cs @@ -0,0 +1,35 @@ +using System; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [Flags] + public enum MLCPF + { + // Not currently supported. + MLDETECTF_MAILNEWS = 0x0001, + + // Not currently supported. + MLDETECTF_BROWSER = 0x0002, + + // Detection result must be valid for conversion and text rendering. + MLDETECTF_VALID = 0x0004, + + // Detection result must be valid for conversion. + MLDETECTF_VALID_NLS = 0x0008, + + //Preserve preferred code page order. + //This is meaningful only if you have set the puiPreferredCodePages parameter in IMultiLanguage3::DetectOutboundCodePage or IMultiLanguage3::DetectOutboundCodePageInIStream. + MLDETECTF_PRESERVE_ORDER = 0x0010, + + // Only return one of the preferred code pages as the detection result. + // This is meaningful only if you have set the puiPreferredCodePages parameter in IMultiLanguage3::DetectOutboundCodePage or IMultiLanguage3::DetectOutboundCodePageInIStream. + MLDETECTF_PREFERRED_ONLY = 0x0020, + + // Filter out graphical symbols and punctuation. + MLDETECTF_FILTER_SPECIALCHAR = 0x0040, + + // Return only Unicode codepages if the euro character is detected. + MLDETECTF_EURO_UTF8 = 0x0080 + } + +} diff --git a/libse/DetectEncoding/Multilang/tagMLDETECTCP.cs b/libse/DetectEncoding/Multilang/tagMLDETECTCP.cs new file mode 100644 index 000000000..95e694fe4 --- /dev/null +++ b/libse/DetectEncoding/Multilang/tagMLDETECTCP.cs @@ -0,0 +1,23 @@ +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + public enum MLDETECTCP + { + // Default setting will be used. + MLDETECTCP_NONE = 0, + + // Input stream consists of 7-bit data. + MLDETECTCP_7BIT = 1, + + // Input stream consists of 8-bit data. + MLDETECTCP_8BIT = 2, + + // Input stream consists of double-byte data. + MLDETECTCP_DBCS = 4, + + // Input stream is an HTML page. + MLDETECTCP_HTML = 8, + + //Not currently supported. + MLDETECTCP_NUMBER = 16 + } +} diff --git a/libse/DetectEncoding/Multilang/tagMLSTR_FLAGS.cs b/libse/DetectEncoding/Multilang/tagMLSTR_FLAGS.cs new file mode 100644 index 000000000..edafa4634 --- /dev/null +++ b/libse/DetectEncoding/Multilang/tagMLSTR_FLAGS.cs @@ -0,0 +1,9 @@ +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + + public enum tagMLSTR_FLAGS + { + MLSTR_READ = 1, + MLSTR_WRITE = 2 + } +} diff --git a/libse/DetectEncoding/Multilang/tagRFC1766INFO.cs b/libse/DetectEncoding/Multilang/tagRFC1766INFO.cs new file mode 100644 index 000000000..d64c40c96 --- /dev/null +++ b/libse/DetectEncoding/Multilang/tagRFC1766INFO.cs @@ -0,0 +1,14 @@ +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct tagRFC1766INFO + { + public uint lcid; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] + public ushort[] wszRfc1766; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x20)] + public ushort[] wszLocaleName; + } +} diff --git a/libse/DetectEncoding/Multilang/tagSCRIPFONTINFO.cs b/libse/DetectEncoding/Multilang/tagSCRIPFONTINFO.cs new file mode 100644 index 000000000..c68303cf7 --- /dev/null +++ b/libse/DetectEncoding/Multilang/tagSCRIPFONTINFO.cs @@ -0,0 +1,12 @@ +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [StructLayout(LayoutKind.Sequential, Pack = 8)] + public struct tagSCRIPFONTINFO + { + public long scripts; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x20)] + public ushort[] wszFont; + } +} diff --git a/libse/DetectEncoding/Multilang/tagSCRIPTINFO.cs b/libse/DetectEncoding/Multilang/tagSCRIPTINFO.cs new file mode 100644 index 000000000..52020bd5c --- /dev/null +++ b/libse/DetectEncoding/Multilang/tagSCRIPTINFO.cs @@ -0,0 +1,17 @@ +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct tagSCRIPTINFO + { + public byte ScriptId; + public uint uiCodePage; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x30)] + public ushort[] wszDescription; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x20)] + public ushort[] wszFixedWidthFont; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x20)] + public ushort[] wszProportionalFont; + } +} diff --git a/libse/DetectEncoding/Multilang/tagSTATSTG.cs b/libse/DetectEncoding/Multilang/tagSTATSTG.cs new file mode 100644 index 000000000..7ee05caf2 --- /dev/null +++ b/libse/DetectEncoding/Multilang/tagSTATSTG.cs @@ -0,0 +1,22 @@ +using System; +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [StructLayout(LayoutKind.Sequential, Pack = 8)] + public struct tagSTATSTG + { + [MarshalAs(UnmanagedType.LPWStr)] + public string pwcsName; + public uint type; + public _ULARGE_INTEGER cbSize; + public _FILETIME mtime; + public _FILETIME ctime; + public _FILETIME atime; + public uint grfMode; + public uint grfLocksSupported; + public Guid clsid; + public uint grfStateBits; + public uint reserved; + } +} diff --git a/libse/DetectEncoding/Multilang/tagUNICODERANGE.cs b/libse/DetectEncoding/Multilang/tagUNICODERANGE.cs new file mode 100644 index 000000000..8ce56617b --- /dev/null +++ b/libse/DetectEncoding/Multilang/tagUNICODERANGE.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core.DetectEncoding.Multilang +{ + [StructLayout(LayoutKind.Sequential, Pack = 2)] + public struct tagUNICODERANGE + { + public ushort wcFrom; + public ushort wcTo; + } +} diff --git a/libse/Dictionaries/NamesList.cs b/libse/Dictionaries/NamesList.cs new file mode 100644 index 000000000..ed52bd715 --- /dev/null +++ b/libse/Dictionaries/NamesList.cs @@ -0,0 +1,258 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.Dictionaries +{ + public class NamesList + { + private readonly string _dictionaryFolder; + private readonly HashSet _namesList; + private readonly HashSet _namesMultiList; + private readonly string _languageName; + + public NamesList(string dictionaryFolder, string languageName, bool useOnlineNamesEtc, string namesEtcUrl) + { + _dictionaryFolder = dictionaryFolder; + _languageName = languageName; + + _namesList = new HashSet(); + _namesMultiList = new HashSet(); + + if (useOnlineNamesEtc && !string.IsNullOrEmpty(namesEtcUrl)) + { + try + { + var xml = Utilities.DownloadString(Configuration.Settings.WordLists.NamesEtcUrl); + var namesDoc = new XmlDocument(); + namesDoc.LoadXml(xml); + LoadNames(_namesList, _namesMultiList, namesDoc); + } + catch (Exception exception) + { + LoadNamesList(Path.Combine(_dictionaryFolder, "names_etc.xml"), _namesList, _namesMultiList); + System.Diagnostics.Debug.WriteLine(exception.Message); + } + } + else + { + LoadNamesList(Path.Combine(_dictionaryFolder, "names_etc.xml"), _namesList, _namesMultiList); + } + + LoadNamesList(GetLocalNamesFileName(), _namesList, _namesMultiList); + + var userFile = GetLocalNamesUserFileName(); + LoadNamesList(userFile, _namesList, _namesMultiList); + UnloadRemovedNames(userFile); + } + + public List GetAllNames() + { + var list = new List(); + foreach (var name in _namesList) + { + list.Add(name); + } + foreach (var name in _namesMultiList) + { + list.Add(name); + } + return list; + } + + public HashSet GetNames() + { + return _namesList; + } + + public HashSet GetMultiNames() + { + return _namesMultiList; + } + + private void UnloadRemovedNames(string fileName) + { + if (string.IsNullOrEmpty(fileName) || !File.Exists(fileName)) + return; + + var namesDoc = new XmlDocument(); + namesDoc.Load(fileName); + if (namesDoc.DocumentElement == null) + return; + + foreach (XmlNode node in namesDoc.DocumentElement.SelectNodes("removed_name")) + { + string s = node.InnerText.Trim(); + if (s.Contains(' ')) + { + if (_namesMultiList.Contains(s)) + _namesMultiList.Remove(s); + } + else if (_namesList.Contains(s)) + { + _namesList.Remove(s); + } + } + } + + private string GetLocalNamesFileName() + { + if (_languageName.Length == 2) + { + string[] files = Directory.GetFiles(_dictionaryFolder, _languageName + "_??_names_etc.xml"); + if (files.Length > 0) + return files[0]; + } + return Path.Combine(_dictionaryFolder, _languageName + "_names_etc.xml"); + } + + private string GetLocalNamesUserFileName() + { + if (_languageName.Length == 2) + { + string[] files = Directory.GetFiles(_dictionaryFolder, _languageName + "_??_names_etc_user.xml"); + if (files.Length > 0) + return files[0]; + } + return Path.Combine(_dictionaryFolder, _languageName + "_names_etc_user.xml"); + } + + private static void LoadNamesList(string fileName, HashSet namesList, HashSet namesMultiList) + { + if (string.IsNullOrEmpty(fileName) || !File.Exists(fileName)) + return; + + var namesDoc = new XmlDocument(); + namesDoc.Load(fileName); + if (namesDoc.DocumentElement == null) + return; + + LoadNames(namesList, namesMultiList, namesDoc); + } + + private static void LoadNames(HashSet namesList, HashSet namesMultiList, XmlDocument namesDoc) + { + foreach (XmlNode node in namesDoc.DocumentElement.SelectNodes("name")) + { + string s = node.InnerText.Trim(); + if (s.Contains(' ') && !namesMultiList.Contains(s)) + { + namesMultiList.Add(s); + } + else if (!namesList.Contains(s)) + { + namesList.Add(s); + } + } + } + + public bool Remove(string name) + { + name = name.Trim(); + if (name.Length > 1 && _namesList.Contains(name) || _namesMultiList.Contains(name)) + { + if (_namesList.Contains(name)) + _namesList.Remove(name); + if (_namesMultiList.Contains(name)) + _namesMultiList.Remove(name); + + var userList = new HashSet(); + var fileName = GetLocalNamesUserFileName(); + LoadNamesList(fileName, userList, userList); + + var namesDoc = new XmlDocument(); + if (File.Exists(fileName)) + { + namesDoc.Load(fileName); + } + else + { + namesDoc.LoadXml(""); + } + + if (userList.Contains(name)) + { + userList.Remove(name); + XmlNode nodeToRemove = null; + foreach (XmlNode node in namesDoc.DocumentElement.SelectNodes("name")) + { + if (node.InnerText == name) + { + nodeToRemove = node; + break; + } + } + if (nodeToRemove != null) + namesDoc.DocumentElement.RemoveChild(nodeToRemove); + } + else + { + XmlNode node = namesDoc.CreateElement("removed_name"); + node.InnerText = name; + namesDoc.DocumentElement.AppendChild(node); + } + namesDoc.Save(fileName); + return true; + } + return false; + } + + public bool Add(string name) + { + name = name.Trim(); + if (name.Length > 1 && name.ContainsLetter()) + { + if (name.Contains(' ')) + { + if (!_namesMultiList.Contains(name)) + _namesMultiList.Add(name); + } + else if (!_namesList.Contains(name)) + { + _namesList.Add(name); + } + + var fileName = GetLocalNamesUserFileName(); + var namesEtcDoc = new XmlDocument(); + if (File.Exists(fileName)) + namesEtcDoc.Load(fileName); + else + namesEtcDoc.LoadXml(""); + + XmlNode de = namesEtcDoc.DocumentElement; + if (de != null) + { + XmlNode node = namesEtcDoc.CreateElement("name"); + node.InnerText = name; + de.AppendChild(node); + namesEtcDoc.Save(fileName); + } + return true; + } + return false; + } + + public bool IsInNamesEtcMultiWordList(string text, string word) + { + if (string.IsNullOrEmpty(text)) + return false; + + text = text.Replace(Environment.NewLine, " "); + text = text.FixExtraSpaces(); + + foreach (string s in _namesMultiList) + { + if (s.Contains(word) && text.Contains(s)) + { + if (s.StartsWith(word + " ", StringComparison.Ordinal) || s.EndsWith(" " + word, StringComparison.Ordinal) || s.Contains(" " + word + " ")) + return true; + if (word == s) + return true; + } + } + return false; + } + + } +} \ No newline at end of file diff --git a/libse/Dictionaries/OcrFixReplaceList.cs b/libse/Dictionaries/OcrFixReplaceList.cs new file mode 100644 index 000000000..52f424ddc --- /dev/null +++ b/libse/Dictionaries/OcrFixReplaceList.cs @@ -0,0 +1,962 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using System.Windows.Forms; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.Dictionaries +{ + public class OcrFixReplaceList + { + private static readonly Regex RegExQuestion = new Regex(@"\S\?[A-ZÆØÅÄÖÉÈÀÙÂÊÎÔÛËÏa-zæøåäöéèàùâêîôûëï]", RegexOptions.Compiled); + private static readonly Regex RegExIandZero = new Regex(@"[a-zæøåöääöéèàùâêîôûëï][I1]", RegexOptions.Compiled); + private static readonly Regex RegExTime1 = new Regex(@"[a-zæøåöääöéèàùâêîôûëï]0", RegexOptions.Compiled); + private static readonly Regex RegExTime2 = new Regex(@"0[a-zæøåöääöéèàùâêîôûëï]", RegexOptions.Compiled); + private static readonly Regex HexNumber = new Regex(@"^#?[\dABDEFabcdef]+$", RegexOptions.Compiled); + private static readonly Regex StartEndEndsWithNumber = new Regex(@"^\d+.+\d$", RegexOptions.Compiled); + + public Dictionary WordReplaceList; + public Dictionary PartialLineWordBoundaryReplaceList; + private readonly Dictionary _partialLineAlwaysReplaceList; + private readonly Dictionary _beginLineReplaceList; + private readonly Dictionary _endLineReplaceList; + private readonly Dictionary _wholeLineReplaceList; + private readonly Dictionary _partialWordReplaceListAlways; + private readonly Dictionary _partialWordReplaceList; + private readonly Dictionary _regExList; + private readonly string _replaceListXmlFileName; + + public OcrFixReplaceList(string replaceListXmlFileName) + { + _replaceListXmlFileName = replaceListXmlFileName; + WordReplaceList = new Dictionary(); + PartialLineWordBoundaryReplaceList = new Dictionary(); + _partialLineAlwaysReplaceList = new Dictionary(); + _beginLineReplaceList = new Dictionary(); + _endLineReplaceList = new Dictionary(); + _wholeLineReplaceList = new Dictionary(); + _partialWordReplaceListAlways = new Dictionary(); + _partialWordReplaceList = new Dictionary(); + _regExList = new Dictionary(); + + var doc = LoadXmlReplaceListDocument(); + var userDoc = LoadXmlReplaceListUserDocument(); + + WordReplaceList = LoadReplaceList(doc, "WholeWords"); + _partialWordReplaceListAlways = LoadReplaceList(doc, "PartialWordsAlways"); + _partialWordReplaceList = LoadReplaceList(doc, "PartialWords"); + PartialLineWordBoundaryReplaceList = LoadReplaceList(doc, "PartialLines"); + _partialLineAlwaysReplaceList = LoadReplaceList(doc, "PartialAlwaysLines"); + _beginLineReplaceList = LoadReplaceList(doc, "BeginLines"); + _endLineReplaceList = LoadReplaceList(doc, "EndLines"); + _wholeLineReplaceList = LoadReplaceList(doc, "WholeLines"); + _regExList = LoadRegExList(doc, "RegularExpressions"); + + foreach (var kp in LoadReplaceList(userDoc, "RemovedWholeWords")) + { + if (WordReplaceList.ContainsKey(kp.Key)) + WordReplaceList.Remove(kp.Key); + } + foreach (var kp in LoadReplaceList(userDoc, "WholeWords")) + { + if (!WordReplaceList.ContainsKey(kp.Key)) + WordReplaceList.Add(kp.Key, kp.Value); + } + + foreach (var kp in LoadReplaceList(userDoc, "RemovedPartialLines")) + { + if (PartialLineWordBoundaryReplaceList.ContainsKey(kp.Key)) + PartialLineWordBoundaryReplaceList.Remove(kp.Key); + } + foreach (var kp in LoadReplaceList(userDoc, "PartialLines")) + { + if (!PartialLineWordBoundaryReplaceList.ContainsKey(kp.Key)) + PartialLineWordBoundaryReplaceList.Add(kp.Key, kp.Value); + } + } + + public static OcrFixReplaceList FromLanguageId(string languageId) + { + return new OcrFixReplaceList(Configuration.DictionariesFolder + languageId + "_OCRFixReplaceList.xml"); + } + + private static Dictionary LoadReplaceList(XmlDocument doc, string name) + { + var list = new Dictionary(); + if (doc.DocumentElement != null) + { + XmlNode node = doc.DocumentElement.SelectSingleNode(name); + if (node != null) + { + foreach (XmlNode item in node.ChildNodes) + { + if (item.Attributes != null && item.Attributes["to"] != null && item.Attributes["from"] != null) + { + string to = item.Attributes["to"].InnerText; + string from = item.Attributes["from"].InnerText; + if (!list.ContainsKey(from)) + list.Add(from, to); + } + } + } + } + return list; + } + + private static Dictionary LoadRegExList(XmlDocument doc, string name) + { + var list = new Dictionary(); + if (doc.DocumentElement != null) + { + XmlNode node = doc.DocumentElement.SelectSingleNode(name); + if (node != null) + { + foreach (XmlNode item in node.ChildNodes) + { + if (item.Attributes != null && item.Attributes["replaceWith"] != null && item.Attributes["find"] != null) + { + string to = item.Attributes["replaceWith"].InnerText; + string from = item.Attributes["find"].InnerText; + if (!list.ContainsKey(from)) + list.Add(from, to); + } + } + } + } + return list; + } + + public string FixOcrErrorViaLineReplaceList(string input) + { + // Whole fromLine + foreach (string from in _wholeLineReplaceList.Keys) + { + if (input == from) + return _wholeLineReplaceList[from]; + } + + string newText = input; + string pre = string.Empty; + if (newText.StartsWith("", StringComparison.Ordinal)) + { + pre += ""; + newText = newText.Remove(0, 3); + } + while (newText.Length > 1 && @" -""['¶(".Contains(newText[0])) + { + pre += newText[0]; + newText = newText.Substring(1); + } + if (newText.StartsWith("", StringComparison.Ordinal)) + { + pre += ""; + newText = newText.Remove(0, 3); + } + + // begin fromLine + var lines = newText.SplitToLines(); + var sb = new StringBuilder(); + foreach (string l in lines) + { + string s = l; + foreach (string from in _beginLineReplaceList.Keys) + { + if (s.Contains(from)) + { + if (s.StartsWith(from)) + s = s.Remove(0, from.Length).Insert(0, _beginLineReplaceList[from]); + if (s.Contains(". " + from)) + s = s.Replace(". " + from, ". " + _beginLineReplaceList[from]); + if (s.Contains("! " + from)) + s = s.Replace("! " + from, "! " + _beginLineReplaceList[from]); + if (s.Contains("? " + from)) + s = s.Replace("? " + from, "? " + _beginLineReplaceList[from]); + if (s.Contains("." + Environment.NewLine + from)) + s = s.Replace(". " + Environment.NewLine + from, ". " + Environment.NewLine + _beginLineReplaceList[from]); + if (s.Contains("! " + Environment.NewLine + from)) + s = s.Replace("! " + Environment.NewLine + from, "! " + Environment.NewLine + _beginLineReplaceList[from]); + if (s.Contains("? " + Environment.NewLine + from)) + s = s.Replace("? " + Environment.NewLine + from, "? " + Environment.NewLine + _beginLineReplaceList[from]); + if (s.StartsWith('"') && !from.StartsWith('"') && s.StartsWith("\"" + from)) + s = s.Replace("\"" + from, "\"" + _beginLineReplaceList[from]); + } + } + sb.AppendLine(s); + } + newText = sb.ToString().TrimEnd('\r').TrimEnd('\n').TrimEnd('\r').TrimEnd('\n'); + newText = pre + newText; + + string post = string.Empty; + if (newText.EndsWith("", StringComparison.Ordinal)) + { + newText = newText.Remove(newText.Length - 4, 4); + post = ""; + } + + foreach (string from in _endLineReplaceList.Keys) + { + if (newText.EndsWith(from, StringComparison.Ordinal)) + { + int position = (newText.Length - from.Length); + newText = newText.Remove(position).Insert(position, _endLineReplaceList[from]); + } + } + newText += post; + + foreach (string from in PartialLineWordBoundaryReplaceList.Keys) + { + if (newText.FastIndexOf(from) >= 0) + newText = ReplaceWord(newText, from, PartialLineWordBoundaryReplaceList[from]); + } + + foreach (string from in _partialLineAlwaysReplaceList.Keys) + { + if (newText.FastIndexOf(from) >= 0) + newText = newText.Replace(from, _partialLineAlwaysReplaceList[from]); + } + + foreach (string findWhat in _regExList.Keys) + { + newText = Regex.Replace(newText, findWhat, _regExList[findWhat], RegexOptions.Multiline); + } + + return newText; + } + + private static string AddToGuessList(List list, string word, int index, string letter, string replaceLetters) + { + if (string.IsNullOrEmpty(word) || index < 0 || index + letter.Length - 1 >= word.Length) + return word; + + string s = word.Remove(index, letter.Length); + if (index >= s.Length) + s += replaceLetters; + else + s = s.Insert(index, replaceLetters); + + if (!list.Contains(s)) + list.Add(s); + + return s; + } + + public IEnumerable CreateGuessesFromLetters(string word) + { + var list = new List(); + foreach (string letter in _partialWordReplaceList.Keys) + { + string s = word; + int i = 0; + while (s.Contains(letter) && i < 10) + { + int index = s.FastIndexOf(letter); + s = AddToGuessList(list, s, index, letter, _partialWordReplaceList[letter]); + AddToGuessList(list, word, index, letter, _partialWordReplaceList[letter]); + i++; + } + s = word; + i = 0; + while (s.Contains(letter) && i < 10) + { + int index = s.LastIndexOf(letter, StringComparison.Ordinal); + s = AddToGuessList(list, s, index, letter, _partialWordReplaceList[letter]); + AddToGuessList(list, word, index, letter, _partialWordReplaceList[letter]); + i++; + } + } + return list; + } + + public string FixCommonWordErrors(string word) + { + if (Configuration.Settings.Tools.OcrFixUseHardcodedRules) + { + word = word.Replace("fi", "fi"); + word = word.Replace("ν", "v"); // NOTE: first 'v' is a special unicode character!!!! + + if (word.Contains('’')) + word = word.Replace('’', '\''); + + if (word.Contains('`')) + word = word.Replace('`', '\''); + + if (word.Contains('‘')) + word = word.Replace('‘', '\''); + + if (word.Contains('—')) + word = word.Replace('—', '-'); + + while (word.Contains("--")) + word = word.Replace("--", "-"); + + if (word.Contains('|')) + word = word.Replace('|', 'l'); + + if (word.Contains("vx/")) + word = word.Replace("vx/", "w"); + + if (word.Contains('¤')) + { + if (Regex.IsMatch(word, "[A-ZÆØÅÄÖÉÈÀÙÂÊÎÔÛËÏa-zæøåäöéèàùâêîôûëï]¤")) + word = word.Replace('¤', 'o'); + } + } + + //always replace list + foreach (string letter in _partialWordReplaceListAlways.Keys) + word = word.Replace(letter, _partialWordReplaceListAlways[letter]); + + string pre = string.Empty; + string post = string.Empty; + + if (word.StartsWith("", StringComparison.Ordinal)) + { + pre += ""; + word = word.Remove(0, 3); + } + while (word.Length > 2 && word.StartsWith(Environment.NewLine, StringComparison.Ordinal)) + { + pre += Environment.NewLine; + word = word.Substring(2); + } + + while (word.Length > 1 && word[0] == '-') + { + pre += "-"; + word = word.Substring(1); + } + while (word.Length > 1 && word[0] == '.') + { + pre += "."; + word = word.Substring(1); + } + while (word.Length > 1 && word[0] == '"') + { + pre += "\""; + word = word.Substring(1); + } + if (word.Length > 1 && word[0] == '(') + { + pre += "("; + word = word.Substring(1); + } + if (word.StartsWith("", StringComparison.Ordinal)) + { + pre += ""; + word = word.Remove(0, 3); + } + while (word.Length > 2 && word.EndsWith(Environment.NewLine)) + { + post += Environment.NewLine; + word = word.Substring(0, word.Length - 2); + } + while (word.Length > 1 && word.EndsWith('"')) + { + post = post + "\""; + word = word.Substring(0, word.Length - 1); + } + while (word.Length > 1 && word.EndsWith('.')) + { + post = post + "."; + word = word.Substring(0, word.Length - 1); + } + while (word.EndsWith(',') && word.Length > 1) + { + post = post + ","; + word = word.Substring(0, word.Length - 1); + } + while (word.EndsWith('?') && word.Length > 1) + { + post = post + "?"; + word = word.Substring(0, word.Length - 1); + } + while (word.EndsWith('!') && word.Length > 1) + { + post = post + "!"; + word = word.Substring(0, word.Length - 1); + } + while (word.EndsWith(')') && word.Length > 1) + { + post = post + ")"; + word = word.Substring(0, word.Length - 1); + } + if (word.EndsWith("", StringComparison.Ordinal)) + { + post = post + ""; + word = word.Remove(word.Length - 4, 4); + } + string preWordPost = pre + word + post; + if (word.Length == 0) + return preWordPost; + + if (word.Contains('?')) + { + var match = RegExQuestion.Match(word); + if (match.Success) + word = word.Insert(match.Index + 2, " "); + } + + foreach (string from in WordReplaceList.Keys) + { + if (word.Length == from.Length) + { + if (word == from) + return pre + WordReplaceList[from] + post; + } + else if (word.Length + post.Length == from.Length) + { + if (string.CompareOrdinal(word + post, from) == 0) + return pre + WordReplaceList[from]; + } + if (pre.Length + word.Length + post.Length == from.Length && string.CompareOrdinal(preWordPost, from) == 0) + { + return WordReplaceList[from]; + } + } + + if (Configuration.Settings.Tools.OcrFixUseHardcodedRules) + { + // uppercase I or 1 inside lowercase fromWord (will be replaced by lowercase L) + word = FixIor1InsideLowerCaseWord(word); + + // uppercase 0 inside lowercase fromWord (will be replaced by lowercase L) + word = Fix0InsideLowerCaseWord(word); + + // uppercase I or 1 inside lowercase fromWord (will be replaced by lowercase L) + word = FixIor1InsideLowerCaseWord(word); + + word = FixLowerCaseLInsideUpperCaseWord(word); // eg. SCARLETTl => SCARLETTI + } + + // Retry fromWord replace list + foreach (string from in WordReplaceList.Keys) + { + if (word.Length == from.Length) + { + if (string.CompareOrdinal(word, from) == 0) + return pre + WordReplaceList[from] + post; + } + else if (word.Length + post.Length == from.Length) + { + if (string.CompareOrdinal(word + post, from) == 0) + return pre + WordReplaceList[from]; + } + if (pre.Length + word.Length + post.Length == from.Length && string.CompareOrdinal(preWordPost, from) == 0) + { + return WordReplaceList[from]; + } + } + + return preWordPost; + } + + public static string FixLowerCaseLInsideUpperCaseWord(string word) + { + if (word.Length > 3 && word.Replace("l", string.Empty).ToUpper() == word.Replace("l", string.Empty)) + { + if (!word.Contains('<') && !word.Contains('>') && !word.Contains('\'')) + { + word = word.Replace('l', 'I'); + } + } + return word; + } + + public static string FixIor1InsideLowerCaseWord(string word) + { + if (StartEndEndsWithNumber.IsMatch(word)) + return word; + + if (word.Contains('2') || + word.Contains('3') || + word.Contains('4') || + word.Contains('5') || + word.Contains('6') || + word.Contains('7') || + word.Contains('8') || + word.Contains('9')) + return word; + + if (HexNumber.IsMatch(word)) + return word; + + if (word.LastIndexOf('I') > 0 || word.LastIndexOf('1') > 0) + { + var match = RegExIandZero.Match(word); + while (match.Success) + { + if (word[match.Index + 1] == 'I' || word[match.Index + 1] == '1') + { + bool doFix = word[match.Index + 1] != 'I' && match.Index >= 1 && word.Substring(match.Index - 1).StartsWith("Mc"); + if (word[match.Index + 1] == 'I' && match.Index >= 2 && word.Substring(match.Index - 2).StartsWith("Mac")) + doFix = false; + + if (doFix) + { + string oldText = word; + word = word.Substring(0, match.Index + 1) + "l"; + if (match.Index + 2 < oldText.Length) + word += oldText.Substring(match.Index + 2); + } + } + match = RegExIandZero.Match(word, match.Index + 1); + } + } + return word; + } + + public static string Fix0InsideLowerCaseWord(string word) + { + if (StartEndEndsWithNumber.IsMatch(word)) + return word; + + if (word.Contains('1') || + word.Contains('2') || + word.Contains('3') || + word.Contains('4') || + word.Contains('5') || + word.Contains('6') || + word.Contains('7') || + word.Contains('8') || + word.Contains('9') || + word.EndsWith("a.m", StringComparison.Ordinal) || + word.EndsWith("p.m", StringComparison.Ordinal) || + word.EndsWith("am", StringComparison.Ordinal) || + word.EndsWith("pm", StringComparison.Ordinal)) + return word; + + if (HexNumber.IsMatch(word)) + return word; + + if (word.LastIndexOf('0') > 0) + { + Match match = RegExTime1.Match(word); + while (match.Success) + { + if (word[match.Index + 1] == '0') + { + string oldText = word; + word = word.Substring(0, match.Index + 1) + "o"; + if (match.Index + 2 < oldText.Length) + word += oldText.Substring(match.Index + 2); + } + match = RegExTime1.Match(word); + } + + match = RegExTime2.Match(word); + while (match.Success) + { + if (word[match.Index] == '0') + { + if (match.Index == 0 || !@"123456789".Contains(word[match.Index - 1])) + { + string oldText = word; + word = word.Substring(0, match.Index) + "o"; + if (match.Index + 1 < oldText.Length) + word += oldText.Substring(match.Index + 1); + } + } + match = RegExTime2.Match(word, match.Index + 1); + } + } + return word; + } + + public string FixCommonWordErrorsQuick(string word) + { + //always replace list + foreach (string letter in _partialWordReplaceListAlways.Keys) + word = word.Replace(letter, _partialWordReplaceListAlways[letter]); + + string pre = string.Empty; + string post = string.Empty; + + if (word.StartsWith("", StringComparison.Ordinal)) + { + pre += ""; + word = word.Remove(0, 3); + } + while (word.StartsWith(Environment.NewLine) && word.Length > 2) + { + pre += Environment.NewLine; + word = word.Substring(2); + } + + while (word.Length > 1 && word[0] == '-') + { + pre += "-"; + word = word.Substring(1); + } + while (word.Length > 1 && word[0] == '.') + { + pre += "."; + word = word.Substring(1); + } + while (word.Length > 1 && word[0] == '"') + { + pre += "\""; + word = word.Substring(1); + } + if (word.Length > 1 && word[0] == '(') + { + pre += "("; + word = word.Substring(1); + } + if (word.StartsWith("", StringComparison.Ordinal)) + { + pre += ""; + word = word.Remove(0, 3); + } + while (word.EndsWith(Environment.NewLine) && word.Length > 2) + { + post += Environment.NewLine; + word = word.Substring(0, word.Length - 2); + } + while (word.EndsWith('"') && word.Length > 1) + { + post = post + "\""; + word = word.Substring(0, word.Length - 1); + } + while (word.EndsWith('.') && word.Length > 1) + { + post = post + "."; + word = word.Substring(0, word.Length - 1); + } + while (word.EndsWith(',') && word.Length > 1) + { + post = post + ","; + word = word.Substring(0, word.Length - 1); + } + while (word.EndsWith('?') && word.Length > 1) + { + post = post + "?"; + word = word.Substring(0, word.Length - 1); + } + while (word.EndsWith('!') && word.Length > 1) + { + post = post + "!"; + word = word.Substring(0, word.Length - 1); + } + while (word.EndsWith(')') && word.Length > 1) + { + post = post + ")"; + word = word.Substring(0, word.Length - 1); + } + if (word.EndsWith("", StringComparison.Ordinal)) + { + post = post + ""; + word = word.Remove(word.Length - 4, 4); + } + + string preWordPost = pre + word + post; + if (word.Length == 0) + return preWordPost; + + foreach (string from in WordReplaceList.Keys) + { + if (word.Length == from.Length) + { + if (string.CompareOrdinal(word, from) == 0) + return pre + WordReplaceList[from] + post; + } + else if (word.Length + post.Length == from.Length) + { + if (string.CompareOrdinal(word + post, from) == 0) + return pre + WordReplaceList[from]; + } + if (pre.Length + word.Length + post.Length == from.Length && string.CompareOrdinal(preWordPost, from) == 0) + { + return WordReplaceList[from]; + } + } + + return preWordPost; + } + + public bool RemoveWordOrPartial(string word) + { + if (word.Contains(' ')) + { + if (DeletePartialLineFromWordList(word)) + { + if (PartialLineWordBoundaryReplaceList.ContainsKey(word)) + PartialLineWordBoundaryReplaceList.Remove(word); + return true; + } + return false; + } + if (DeleteWordFromWordList(word)) + { + if (WordReplaceList.ContainsKey(word)) + WordReplaceList.Remove(word); + return true; + } + return false; + } + + private bool DeleteWordFromWordList(string fromWord) + { + const string replaceListName = "WholeWords"; + + var doc = LoadXmlReplaceListDocument(); + var list = LoadReplaceList(doc, replaceListName); + + var userDoc = LoadXmlReplaceListUserDocument(); + var userList = LoadReplaceList(userDoc, replaceListName); + + return DeleteFromList(fromWord, userDoc, replaceListName, "Word", list, userList); + } + + private bool DeletePartialLineFromWordList(string fromWord) + { + const string replaceListName = "PartialLines"; + + var doc = LoadXmlReplaceListDocument(); + var list = LoadReplaceList(doc, replaceListName); + + var userDoc = LoadXmlReplaceListUserDocument(); + var userList = LoadReplaceList(userDoc, replaceListName); + + return DeleteFromList(fromWord, userDoc, replaceListName, "LinePart", list, userList); + } + + private bool DeleteFromList(string word, XmlDocument userDoc, string replaceListName, string elementName, Dictionary dictionary, Dictionary userDictionary) + { + if (dictionary == null) + throw new ArgumentNullException("dictionary"); + if (userDictionary == null) + throw new ArgumentNullException("userDictionary"); + + bool removed = false; + if (userDictionary.ContainsKey((word))) + { + userDictionary.Remove(word); + XmlNode wholeWordsNode = userDoc.DocumentElement.SelectSingleNode(replaceListName); + if (wholeWordsNode != null) + { + wholeWordsNode.RemoveAll(); + foreach (var kvp in userDictionary) + { + XmlNode newNode = userDoc.CreateNode(XmlNodeType.Element, elementName, null); + XmlAttribute aFrom = userDoc.CreateAttribute("from"); + XmlAttribute aTo = userDoc.CreateAttribute("to"); + aFrom.InnerText = kvp.Key; + aTo.InnerText = kvp.Value; + newNode.Attributes.Append(aTo); + newNode.Attributes.Append(aFrom); + wholeWordsNode.AppendChild(newNode); + } + userDoc.Save(ReplaceListXmlFileNameUser); + removed = true; + } + } + if (dictionary.ContainsKey((word))) + { + XmlNode wholeWordsNode = userDoc.DocumentElement.SelectSingleNode("Removed" + replaceListName); + if (wholeWordsNode != null) + { + XmlNode newNode = userDoc.CreateNode(XmlNodeType.Element, elementName, null); + XmlAttribute aFrom = userDoc.CreateAttribute("from"); + XmlAttribute aTo = userDoc.CreateAttribute("to"); + aFrom.InnerText = word; + aTo.InnerText = string.Empty; + newNode.Attributes.Append(aTo); + newNode.Attributes.Append(aFrom); + wholeWordsNode.AppendChild(newNode); + userDoc.Save(ReplaceListXmlFileNameUser); + removed = true; + } + } + return removed; + } + + private XmlDocument LoadXmlReplaceListDocument() + { + const string xmlText = ""; + var doc = new XmlDocument(); + if (File.Exists(_replaceListXmlFileName)) + { + try + { + doc.Load(_replaceListXmlFileName); + } + catch + { + doc.LoadXml(xmlText); + } + } + else + { + doc.LoadXml(xmlText); + } + return doc; + } + + private string ReplaceListXmlFileNameUser + { + get { return Path.Combine(Path.GetDirectoryName(_replaceListXmlFileName), Path.GetFileNameWithoutExtension(_replaceListXmlFileName) + "_User" + Path.GetExtension(_replaceListXmlFileName)); } + } + + private XmlDocument LoadXmlReplaceListUserDocument() + { + const string xmlText = ""; + var doc = new XmlDocument(); + if (File.Exists(ReplaceListXmlFileNameUser)) + { + try + { + doc.Load(ReplaceListXmlFileNameUser); + } + catch + { + doc.LoadXml(xmlText); + } + } + else + { + doc.LoadXml(xmlText); + } + return doc; + } + + public bool AddWordOrPartial(string fromWord, string toWord) + { + if (fromWord.Contains(' ')) + { + if (SavePartialLineToWordList(fromWord, toWord)) + { + if (!PartialLineWordBoundaryReplaceList.ContainsKey(fromWord)) + PartialLineWordBoundaryReplaceList.Add(fromWord, toWord); + return true; + } + return false; + } + if (SaveWordToWordList(fromWord, toWord)) + { + if (!WordReplaceList.ContainsKey(fromWord)) + WordReplaceList.Add(fromWord, toWord); + return true; + } + return false; + } + + private bool SaveWordToWordList(string fromWord, string toWord) + { + const string replaceListName = "WholeWords"; + + var doc = LoadXmlReplaceListDocument(); + var list = LoadReplaceList(doc, replaceListName); + + var userDoc = LoadXmlReplaceListUserDocument(); + var userList = LoadReplaceList(userDoc, replaceListName); + + return SaveToList(fromWord, toWord, userDoc, replaceListName, "Word", list, userList); + } + + private bool SavePartialLineToWordList(string fromWord, string toWord) + { + const string replaceListName = "PartialLines"; + + var doc = LoadXmlReplaceListDocument(); + var list = LoadReplaceList(doc, replaceListName); + + var userDoc = LoadXmlReplaceListUserDocument(); + var userList = LoadReplaceList(userDoc, replaceListName); + + return SaveToList(fromWord, toWord, userDoc, replaceListName, "LinePart", list, userList); + } + + private bool SaveToList(string fromWord, string toWord, XmlDocument userDoc, string replaceListName, string elementName, Dictionary dictionary, Dictionary userDictionary) + { + if (dictionary == null) + throw new ArgumentNullException("dictionary"); + if (userDictionary == null) + throw new ArgumentNullException("userDictionary"); + if (userDictionary.ContainsKey(fromWord)) + return false; + + userDictionary.Add(fromWord, toWord); + XmlNode wholeWordsNode = userDoc.DocumentElement.SelectSingleNode(replaceListName); + if (wholeWordsNode != null) + { + XmlNode newNode = userDoc.CreateNode(XmlNodeType.Element, elementName, null); + XmlAttribute aFrom = userDoc.CreateAttribute("from"); + XmlAttribute aTo = userDoc.CreateAttribute("to"); + aTo.InnerText = toWord; + aFrom.InnerText = fromWord; + newNode.Attributes.Append(aFrom); + newNode.Attributes.Append(aTo); + wholeWordsNode.AppendChild(newNode); + userDoc.Save(ReplaceListXmlFileNameUser); + } + return true; + } + + public void AddToWholeLineList(string fromLine, string toLine) + { + try + { + var userDocument = LoadXmlReplaceListUserDocument(); + if (!_wholeLineReplaceList.ContainsKey(fromLine)) + _wholeLineReplaceList.Add(fromLine, toLine); + XmlNode wholeWordsNode = userDocument.DocumentElement.SelectSingleNode("WholeLines"); + if (wholeWordsNode != null) + { + XmlNode newNode = userDocument.CreateNode(XmlNodeType.Element, "Line", null); + XmlAttribute aFrom = userDocument.CreateAttribute("from"); + XmlAttribute aTo = userDocument.CreateAttribute("to"); + aTo.InnerText = toLine; + aFrom.InnerText = fromLine; + newNode.Attributes.Append(aFrom); + newNode.Attributes.Append(aTo); + wholeWordsNode.AppendChild(newNode); + userDocument.Save(_replaceListXmlFileName); + } + } + catch (Exception exception) + { + MessageBox.Show(exception + Environment.NewLine + exception.StackTrace); + } + } + + public static string ReplaceWord(string text, string word, string newWord) + { + var sb = new StringBuilder(); + if (word != null && text != null && text.Contains(word)) + { + const string startChars = @" ¡¿<>-""”“()[]'‘`´¶♪¿¡.…—!?,:;/"; + int appendFrom = 0; + for (int i = 0; i < text.Length; i++) + { + if (text.Substring(i).StartsWith(word) && i >= appendFrom) + { + bool startOk = i == 0; + if (!startOk) + startOk = (startChars + Environment.NewLine).Contains(text[i - 1]); + if (!startOk && word.StartsWith(' ')) + startOk = true; + if (startOk) + { + bool endOk = (i + word.Length == text.Length); + if (!endOk) + endOk = (startChars + Environment.NewLine).Contains(text[i + word.Length]); + if (!endOk) + endOk = newWord.EndsWith(' '); + if (endOk) + { + sb.Append(newWord); + appendFrom = i + word.Length; + } + } + } + if (i >= appendFrom) + sb.Append(text[i]); + } + } + return sb.ToString(); + } + + } +} \ No newline at end of file diff --git a/libse/Enums/FindType.cs b/libse/Enums/FindType.cs new file mode 100644 index 000000000..6ff9f8bc9 --- /dev/null +++ b/libse/Enums/FindType.cs @@ -0,0 +1,9 @@ +namespace Nikse.Core.Enums +{ + public enum FindType + { + Normal, + CaseSensitive, + RegEx + } +} \ No newline at end of file diff --git a/libse/Enums/SelectionChoice.cs b/libse/Enums/SelectionChoice.cs new file mode 100644 index 000000000..3b172e3af --- /dev/null +++ b/libse/Enums/SelectionChoice.cs @@ -0,0 +1,9 @@ +namespace Nikse.Core.Enums +{ + public enum SelectionChoice + { + SelectionOnly, + AllLines, + SelectionAndForward, + } +} \ No newline at end of file diff --git a/libse/Enums/SpellCheckAction.cs b/libse/Enums/SpellCheckAction.cs new file mode 100644 index 000000000..4af5d45fe --- /dev/null +++ b/libse/Enums/SpellCheckAction.cs @@ -0,0 +1,15 @@ +namespace Nikse.Core.Enums +{ + public enum SpellCheckAction + { + Change, + ChangeAll, + Skip, + SkipAll, + AddToDictionary, + Abort, + ChangeLanguage, + AddToNamesEtc, + ChangeWholeText + } +} \ No newline at end of file diff --git a/libse/Enums/SubtitleSortCriteria.cs b/libse/Enums/SubtitleSortCriteria.cs new file mode 100644 index 000000000..a336583e8 --- /dev/null +++ b/libse/Enums/SubtitleSortCriteria.cs @@ -0,0 +1,17 @@ +namespace Nikse.Core.Enums +{ + public enum SubtitleSortCriteria + { + Number, + StartTime, + EndTime, + Duration, + Text, + TextMaxLineLength, + TextTotalLength, + TextNumberOfLines, + TextCharactersPerSeconds, + WordsPerMinute, + Style, + } +} \ No newline at end of file diff --git a/libse/FastBitmap.cs b/libse/FastBitmap.cs new file mode 100644 index 000000000..ee1ac1fab --- /dev/null +++ b/libse/FastBitmap.cs @@ -0,0 +1,110 @@ +//Downloaded from Visual C# Kicks - http://www.vcskicks.com/ + +using System; +using System.Drawing; +using System.Drawing.Imaging; + +namespace Nikse.SubtitleEdit.Core +{ + unsafe public class FastBitmap + { + private struct PixelData + { + public byte Blue; + public byte Green; + public byte Red; + public byte Alpha; + + public override string ToString() + { + return "(" + Alpha + ", " + Red + ", " + Green + ", " + Blue + ")"; + } + } + + public int Width { get; set; } + public int Height { get; set; } + + private readonly Bitmap _workingBitmap; + private int _width; + private BitmapData _bitmapData; + private Byte* _pBase = null; + + public FastBitmap(Bitmap inputBitmap) + { + _workingBitmap = inputBitmap; + + if (_workingBitmap.PixelFormat != PixelFormat.Format32bppArgb && + Environment.OSVersion.Version.Major < 6 && Configuration.Settings.General.SubtitleFontName == Utilities.WinXP2KUnicodeFontName) // 6 == Vista/Win2008Server/Win7 + { // WinXp Fix + var newBitmap = new Bitmap(_workingBitmap.Width, _workingBitmap.Height, PixelFormat.Format32bppArgb); + for (int y = 0; y < _workingBitmap.Height; y++) + for (int x = 0; x < _workingBitmap.Width; x++) + newBitmap.SetPixel(x, y, _workingBitmap.GetPixel(x, y)); + _workingBitmap = newBitmap; + } + + Width = inputBitmap.Width; + Height = inputBitmap.Height; + } + + public void LockImage() + { + var bounds = new Rectangle(Point.Empty, _workingBitmap.Size); + + _width = bounds.Width * sizeof(PixelData); + if (_width % 4 != 0) _width = 4 * (_width / 4 + 1); + + //Lock Image + _bitmapData = _workingBitmap.LockBits(bounds, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); + _pBase = (Byte*)_bitmapData.Scan0.ToPointer(); + } + + private PixelData* _pixelData = null; + + public Color GetPixel(int x, int y) + { + _pixelData = (PixelData*)(_pBase + y * _width + x * sizeof(PixelData)); + return Color.FromArgb(_pixelData->Alpha, _pixelData->Red, _pixelData->Green, _pixelData->Blue); + } + + public Color GetPixelNext() + { + _pixelData++; + return Color.FromArgb(_pixelData->Alpha, _pixelData->Red, _pixelData->Green, _pixelData->Blue); + } + + public void SetPixel(int x, int y, Color color) + { + var data = (PixelData*)(_pBase + y * _width + x * sizeof(PixelData)); + data->Alpha = color.A; + data->Red = color.R; + data->Green = color.G; + data->Blue = color.B; + } + + public void SetPixel(int x, int y, Color color, int length) + { + var data = (PixelData*)(_pBase + y * _width + x * sizeof(PixelData)); + for (int i = 0; i < length; i++) + { + data->Alpha = color.A; + data->Red = color.R; + data->Green = color.G; + data->Blue = color.B; + data++; + } + } + + public Bitmap GetBitmap() + { + return _workingBitmap; + } + + public void UnlockImage() + { + _workingBitmap.UnlockBits(_bitmapData); + _bitmapData = null; + _pBase = null; + } + } +} \ No newline at end of file diff --git a/libse/FastFileStream.cs b/libse/FastFileStream.cs new file mode 100644 index 000000000..7f9024148 --- /dev/null +++ b/libse/FastFileStream.cs @@ -0,0 +1,104 @@ +using System.IO; + +namespace Nikse.SubtitleEdit.Core +{ + /// + /// Exposes a around a file, supporting synchronous read operations. + /// + internal class FastFileStream : FileStream + { + private readonly long _length; + private long _position; + + /// + /// Initializes a new instance of the class with the specified path. + /// + /// A relative or absolute path for the file that the current object will encapsulate. + public FastFileStream(string path) + : base(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite) + { + _length = base.Length; + } + + /// + /// Gets the length in bytes of the stream. + /// + public override long Length + { + get + { + return _length; + } + } + + /// + /// Gets or sets the current position of the stream. + /// + public override long Position + { + get + { + return _position; + } + set + { + Seek(value, SeekOrigin.Begin); + } + } + + /// + /// Sets the current position of this stream to the given value. + /// + /// The point relative to from which to begin seeking. + /// Specifies the beginning, the end, or the current position as a reference point for origin, using a value of type . + /// The new position in the stream. + public override long Seek(long offset, SeekOrigin origin) + { + switch (origin) + { + case SeekOrigin.Begin: + if (_position != offset) + { + _position = offset; + base.Seek(offset, origin); + } + break; + case SeekOrigin.Current: + if (_position != _position + offset) + { + _position += offset; + base.Seek(offset, origin); + } + break; + default: + _position = base.Seek(offset, origin); + break; + } + return _position; + } + + /// + /// Reads a block of bytes from the stream and writes the data in a given buffer. + /// + /// When this method returns, contains the specified byte array with the values between and ( + - 1) replaced by the bytes read from the current source. + /// The byte offset in at which the read bytes will be placed. + /// The maximum number of bytes to read. + /// The total number of bytes read into the buffer. This might be less than the number of bytes requested if that number of bytes are not currently available, or zero if the end of the stream is reached. + public override int Read(byte[] array, int offset, int count) + { + var bytesRead = base.Read(array, offset, count); + _position += bytesRead; + return bytesRead; + } + + /// + /// Reads a byte from the file and advances the read position one byte. + /// + /// The byte, cast to an Int32, or -1 if the end of the stream has been reached. + public override int ReadByte() + { + _position++; + return base.ReadByte(); + } + } +} \ No newline at end of file diff --git a/libse/FileUtil.cs b/libse/FileUtil.cs new file mode 100644 index 000000000..75f3ec925 --- /dev/null +++ b/libse/FileUtil.cs @@ -0,0 +1,331 @@ +using System; +using System.IO; +using System.Text; +using System.Windows.Forms; +using Nikse.SubtitleEdit.Core.TransportStream; +using Nikse.SubtitleEdit.Core.VobSub; + +namespace Nikse.SubtitleEdit.Core +{ + /// + /// File related utilities. + /// + public static class FileUtil + { + /// + /// Opens a binary file in read/write shared mode, reads the contents of the file into a + /// byte array, and then closes the file. + /// + /// The file to open for reading. + /// A byte array containing the contents of the file. + public static byte[] ReadAllBytesShared(string path) + { + using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + var index = 0; + var fileLength = fs.Length; + if (fileLength > Int32.MaxValue) + throw new IOException("File too long"); + var count = (int)fileLength; + var bytes = new byte[count]; + while (count > 0) + { + var n = fs.Read(bytes, index, count); + if (n == 0) + throw new InvalidOperationException("End of file reached before expected"); + index += n; + count -= n; + } + return bytes; + } + } + + /// + /// Opens an existing file for reading, and allow the user to retry if it fails. + /// + /// The file to be opened for reading. + /// A read-only on the specified path. + public static FileStream RetryOpenRead(string path) + { + FileStream fs = null; + while (fs == null) + { + try + { + fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + } + catch (IOException ex) + { + var result = MessageBox.Show(string.Format("An error occured while opening file: {0}", ex.Message), string.Empty, MessageBoxButtons.RetryCancel); + if (result == DialogResult.Cancel) + { + return null; + } + } + } + return fs; + } + + public static bool IsZip(string fileName) + { + using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + var buffer = new byte[4]; + var count = fs.Read(buffer, 0, buffer.Length); + if (count != buffer.Length) + return false; + return buffer[0] == 0x50 // P + && buffer[1] == 0x4B // K + && buffer[2] == 0x03 // (ETX) + && buffer[3] == 0x04; // (EOT) + } + } + + public static bool IsRar(string fileName) + { + using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + var buffer = new byte[4]; + var count = fs.Read(buffer, 0, buffer.Length); + if (count != buffer.Length) + return false; + return buffer[0] == 0x52 // R + && buffer[1] == 0x61 // a + && buffer[2] == 0x72 // r + && buffer[3] == 0x21; // ! + } + } + + public static bool IsPng(string fileName) + { + using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + var buffer = new byte[8]; + var count = fs.Read(buffer, 0, buffer.Length); + if (count != buffer.Length) + return false; + return buffer[0] == 137 + && buffer[1] == 80 + && buffer[2] == 78 + && buffer[3] == 71 + && buffer[4] == 13 + && buffer[5] == 10 + && buffer[6] == 26 + && buffer[7] == 10; + } + } + + public static bool IsSrr(string fileName) + { + using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + var buffer = new byte[3]; + var count = fs.Read(buffer, 0, buffer.Length); + if (count != buffer.Length) + return false; + return buffer[0] == 0x69 + && buffer[1] == 0x69 + && buffer[2] == 0x69; + } + } + + public static bool IsJpg(string fileName) + { + // jpeg header - always starts with FFD8 (Start Of Image marker) + FF + a uknown byte (most often E0 or E1 though) + using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + var buffer = new byte[3]; + var count = fs.Read(buffer, 0, buffer.Length); + if (count != buffer.Length) + return false; + + return buffer[0] == 0xFF + && buffer[1] == 0xD8 + && buffer[2] == 0xFF; + } + } + + public static bool IsTorrentFile(string fileName) + { + using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + var buffer = new byte[11]; + fs.Read(buffer, 0, buffer.Length); + return Encoding.ASCII.GetString(buffer, 0, buffer.Length) == "d8:announce"; + } + } + + public static bool IsBluRaySup(string fileName) + { + using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + var buffer = new byte[2]; + fs.Read(buffer, 0, buffer.Length); + return buffer[0] == 0x50 // P + && buffer[1] == 0x47; // G + } + } + + public static bool IsTransportStream(string fileName) + { + using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + var buffer = new byte[3761]; + var count = fs.Read(buffer, 0, buffer.Length); + if (count != buffer.Length) + return false; + + return (buffer[0] == 0x47 && buffer[188] == 0x47) || // 47hex (71 dec or 'G') == TS sync byte + (buffer[0] == 0x54 && buffer[1] == 0x46 && buffer[2] == 0x72 && buffer[3760] == 0x47); // Topfield REC TS file + } + } + + public static bool IsM2TransportStream(string fileName) + { + using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + var tsp = new TransportStreamParser(); + tsp.DetectFormat(fs); + return tsp.IsM2TransportStream; + } + } + + public static bool IsMpeg2PrivateStream2(string fileName) + { + using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + var buffer = new byte[4]; + fs.Read(buffer, 0, buffer.Length); + return VobSubParser.IsPrivateStream2(buffer, 0); + } + } + + public static bool IsVobSub(string fileName) + { + using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + var buffer = new byte[4]; + fs.Read(buffer, 0, buffer.Length); + return VobSubParser.IsMpeg2PackHeader(buffer) + || VobSubParser.IsPrivateStream1(buffer, 0); + } + } + + public static bool IsSpDvdSup(string fileName) + { + using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + var buffer = new byte[SpHeader.SpHeaderLength]; + if (fs.Read(buffer, 0, buffer.Length) != buffer.Length) + { + return false; + } + + var header = new SpHeader(buffer); + if (header.Identifier != "SP" || header.NextBlockPosition < 5) + { + return false; + } + + buffer = new byte[header.NextBlockPosition]; + if (fs.Read(buffer, 0, buffer.Length) != buffer.Length) + { + return false; + } + + buffer = new byte[SpHeader.SpHeaderLength]; + if (fs.Read(buffer, 0, buffer.Length) != buffer.Length) + { + return false; + } + + header = new SpHeader(buffer); + return header.Identifier == "SP"; + } + } + + /// + /// Checks if file is an MXF file + /// + /// Input file + /// true if file is an MXF file, otherwise false + public static bool IsMaterialExchangeFormat(string fileName) + { + using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + var buffer = new byte[65536]; + var count = fs.Read(buffer, 0, buffer.Length); + if (count < 100) + return false; + + for (int i = 0; i < count - 11; i++) + { + //Header Partition PackId = 06 0E 2B 34 02 05 01 01 0D 01 02 + if (buffer[i + 00] == 0x06 && + buffer[i + 01] == 0x0E && + buffer[i + 02] == 0x2B && + buffer[i + 03] == 0x34 && + buffer[i + 04] == 0x02 && + buffer[i + 05] == 0x05 && + buffer[i + 06] == 0x01 && + buffer[i + 07] == 0x01 && + buffer[i + 08] == 0x0D && + buffer[i + 09] == 0x01 && + buffer[i + 10] == 0x02) + { + return true; + } + } + } + return false; + } + + public static bool HasUtf8Bom(string fileName) + { + using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + var buffer = new byte[3]; + fs.Read(buffer, 0, buffer.Length); + return buffer[0] == 0xef && buffer[1] == 0xbb && buffer[2] == 0xbf; + } + } + + public static bool IsSubtitleFileAllBinaryZeroes(string fileName) + { + using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + if (fs.Length < 10) + return false; // too short to be a proper subtitle file + + int numberOfBytes = 1; + var buffer = new byte[1024]; + while (numberOfBytes > 0) + { + numberOfBytes = fs.Read(buffer, 0, buffer.Length); + for (int i = 0; i < numberOfBytes; i++) + { + if (buffer[i] > 0) + { + return false; + } + } + } + } + return true; + } + + public static bool IsFile(string path) + { + if (!Path.IsPathRooted(path)) + return false; + return ((File.GetAttributes(path) & FileAttributes.Directory) != FileAttributes.Directory); + } + + public static bool IsDirectory(string path) + { + if (!Path.IsPathRooted(path)) + return false; + return ((File.GetAttributes(path) & FileAttributes.Directory) == FileAttributes.Directory); + } + } +} \ No newline at end of file diff --git a/libse/FindReplaceDialogHelper.cs b/libse/FindReplaceDialogHelper.cs new file mode 100644 index 000000000..c23544ff9 --- /dev/null +++ b/libse/FindReplaceDialogHelper.cs @@ -0,0 +1,232 @@ +using System.Text.RegularExpressions; +using System.Windows.Forms; +using Nikse.Core; +using Nikse.Core.Enums; + +namespace Nikse.SubtitleEdit.Core +{ + public class FindReplaceDialogHelper + { + private readonly string _findText = string.Empty; + private readonly string _replaceText = string.Empty; + private readonly Regex _regEx; + private int _findTextLenght; + + public bool Success { get; set; } + public FindType FindType { get; set; } + public int SelectedIndex { get; set; } + public int SelectedPosition { get; set; } + public int WindowPositionLeft { get; set; } + public int WindowPositionTop { get; set; } + public int StartLineIndex { get; set; } + public bool MatchInOriginal { get; set; } + + public int FindTextLength + { + get + { + return _findTextLenght; + } + } + + public string FindText + { + get + { + return _findText; + } + } + + public string ReplaceText + { + get + { + return _replaceText; + } + } + + public FindReplaceDialogHelper(FindType findType, string findText, Regex regEx, string replaceText, int left, int top, int startLineIndex) + { + FindType = findType; + _findText = findText; + _replaceText = replaceText; + _regEx = regEx; + _findTextLenght = findText.Length; + WindowPositionLeft = left; + WindowPositionTop = top; + StartLineIndex = startLineIndex; + } + + public bool Find(Subtitle subtitle, Subtitle originalSubtitle, int startIndex) + { + return FindNext(subtitle, originalSubtitle, startIndex, 0, Configuration.Settings.General.AllowEditOfOriginalSubtitle); + } + + public bool Find(TextBox textBox, int startIndex) + { + return FindNext(textBox, startIndex); + } + + private int FindPositionInText(string text, int startIndex) + { + if (startIndex >= text.Length && !(FindType == FindType.RegEx && startIndex == 0)) + return -1; + + switch (FindType) + { + case FindType.Normal: + return (text.IndexOf(_findText, startIndex, System.StringComparison.OrdinalIgnoreCase)); + case FindType.CaseSensitive: + return (text.IndexOf(_findText, startIndex, System.StringComparison.Ordinal)); + case FindType.RegEx: + { + Match match = _regEx.Match(text, startIndex); + if (match.Success) + { + string groupName = Utilities.GetRegExGroup(_findText); + if (groupName != null && match.Groups[groupName] != null && match.Groups[groupName].Success) + { + _findTextLenght = match.Groups[groupName].Length; + return match.Groups[groupName].Index; + } + _findTextLenght = match.Length; + return match.Index; + } + return -1; + } + } + return -1; + } + + public bool FindNext(Subtitle subtitle, Subtitle originalSubtitle, int startIndex, int position, bool allowEditOfOriginalSubtitle) + { + Success = false; + int index = 0; + if (position < 0) + position = 0; + foreach (Paragraph p in subtitle.Paragraphs) + { + if (index >= startIndex) + { + int pos = 0; + if (!MatchInOriginal) + { + pos = FindPositionInText(p.Text, position); + if (pos >= 0) + { + MatchInOriginal = false; + SelectedIndex = index; + SelectedPosition = pos; + Success = true; + return true; + } + position = 0; + } + MatchInOriginal = false; + + if (originalSubtitle != null && allowEditOfOriginalSubtitle) + { + Paragraph o = Utilities.GetOriginalParagraph(index, p, originalSubtitle.Paragraphs); + if (o != null) + { + pos = FindPositionInText(o.Text, position); + if (pos >= 0) + { + MatchInOriginal = true; + SelectedIndex = index; + SelectedPosition = pos; + Success = true; + return true; + } + } + } + } + index++; + } + return false; + } + + public static ContextMenu GetRegExContextMenu(TextBox textBox) + { + var cm = new ContextMenu(); + var l = Configuration.Settings.Language.RegularExpressionContextMenu; + cm.MenuItems.Add(l.WordBoundary, delegate { textBox.SelectedText = "\\b"; }); + cm.MenuItems.Add(l.NonWordBoundary, delegate { textBox.SelectedText = "\\B"; }); + cm.MenuItems.Add(l.NewLine, delegate { textBox.SelectedText = "\\r\\n"; }); + cm.MenuItems.Add(l.AnyDigit, delegate { textBox.SelectedText = "\\d"; }); + cm.MenuItems.Add(l.NonDigit, delegate { textBox.SelectedText = "\\D"; }); + cm.MenuItems.Add(l.AnyCharacter, delegate { textBox.SelectedText = "."; }); + cm.MenuItems.Add(l.AnyWhitespace, delegate { textBox.SelectedText = "\\s"; }); + cm.MenuItems.Add(l.NonSpaceCharacter, delegate { textBox.SelectedText = "\\S"; }); + cm.MenuItems.Add(l.ZeroOrMore, delegate { textBox.SelectedText = "*"; }); + cm.MenuItems.Add(l.OneOrMore, delegate { textBox.SelectedText = "+"; }); + cm.MenuItems.Add(l.InCharacterGroup, delegate { textBox.SelectedText = "[test]"; }); + cm.MenuItems.Add(l.NotInCharacterGroup, delegate { textBox.SelectedText = "[^test]"; }); + return cm; + } + + public static ContextMenu GetRegExContextMenu(ComboBox comboBox) + { + var cm = new ContextMenu(); + var l = Configuration.Settings.Language.RegularExpressionContextMenu; + cm.MenuItems.Add(l.WordBoundary, delegate { comboBox.SelectedText = "\\b"; }); + cm.MenuItems.Add(l.NonWordBoundary, delegate { comboBox.SelectedText = "\\B"; }); + cm.MenuItems.Add(l.NewLine, delegate { comboBox.SelectedText = "\\r\\n"; }); + cm.MenuItems.Add(l.AnyDigit, delegate { comboBox.SelectedText = "\\d"; }); + cm.MenuItems.Add(l.NonDigit, delegate { comboBox.SelectedText = "\\D"; }); + cm.MenuItems.Add(l.AnyCharacter, delegate { comboBox.SelectedText = "."; }); + cm.MenuItems.Add(l.AnyWhitespace, delegate { comboBox.SelectedText = "\\s"; }); + cm.MenuItems.Add(l.NonSpaceCharacter, delegate { comboBox.SelectedText = "\\S"; }); + cm.MenuItems.Add(l.ZeroOrMore, delegate { comboBox.SelectedText = "*"; }); + cm.MenuItems.Add(l.OneOrMore, delegate { comboBox.SelectedText = "+"; }); + cm.MenuItems.Add(l.InCharacterGroup, delegate { comboBox.SelectedText = "[test]"; }); + cm.MenuItems.Add(l.NotInCharacterGroup, delegate { comboBox.SelectedText = "[^test]"; }); + return cm; + } + + public static ContextMenu GetReplaceTextContextMenu(TextBox textBox) + { + var cm = new ContextMenu(); + cm.MenuItems.Add(Configuration.Settings.Language.RegularExpressionContextMenu.NewLineShort, delegate { textBox.SelectedText = "\\n"; }); + return cm; + } + + public bool FindNext(TextBox textBox, int startIndex) + { + Success = false; + startIndex++; + if (startIndex < textBox.Text.Length) + { + if (FindType == FindType.RegEx) + { + Match match = _regEx.Match(textBox.Text, startIndex); + if (match.Success) + { + string groupName = Utilities.GetRegExGroup(_findText); + if (groupName != null && match.Groups[groupName] != null && match.Groups[groupName].Success) + { + _findTextLenght = match.Groups[groupName].Length; + SelectedIndex = match.Groups[groupName].Index; + } + else + { + _findTextLenght = match.Length; + SelectedIndex = match.Index; + } + Success = true; + } + return match.Success; + } + string searchText = textBox.Text.Substring(startIndex); + int pos = FindPositionInText(searchText, 0); + if (pos >= 0) + { + SelectedIndex = pos + startIndex; + return true; + } + } + return false; + } + + } +} diff --git a/libse/Forms/CheckForUpdatesHelper.cs b/libse/Forms/CheckForUpdatesHelper.cs new file mode 100644 index 000000000..13c096c88 --- /dev/null +++ b/libse/Forms/CheckForUpdatesHelper.cs @@ -0,0 +1,186 @@ +using System; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; + +namespace Nikse.SubtitleEdit.Core.Forms +{ + public class CheckForUpdatesHelper + { + private readonly static Regex regex = new Regex(@"\d\.\d", RegexOptions.Compiled); // 3.4.0 (xth June 2014) + + //private const string ReleasesUrl = "https://api.github.com/repos/SubtitleEdit/subtitleedit/releases"; + private const string ChangeLogUrl = "https://raw.githubusercontent.com/SubtitleEdit/subtitleedit/master/Changelog.txt"; + + //private string _jsonReleases; + private string _changeLog; + private int _successCount; + + public string Error { get; set; } + public bool Done + { + get + { + return _successCount == 1; + } + } + public string LatestVersionNumber { get; set; } + public string LatestChangeLog { get; set; } + + private void StartDownloadString(string url, string contentType, AsyncCallback callback) + { + try + { + var request = (HttpWebRequest)WebRequest.Create(url); + request.UserAgent = "SubtitleEdit"; + request.ContentType = contentType; + request.Timeout = Timeout.Infinite; + request.Method = "GET"; + request.AllowAutoRedirect = true; + request.Accept = contentType; + request.BeginGetResponse(callback, request); + } + catch (Exception exception) + { + if (Error == null) + { + Error = exception.Message; + } + } + } + + //void FinishWebRequestReleases(IAsyncResult result) + //{ + // try + // { + // _jsonReleases = GetStringFromResponse(result); + // } + // catch (Exception exception) + // { + // if (Error == null) + // { + // Error = exception.Message; + // } + // } + //} + + private void FinishWebRequestChangeLog(IAsyncResult result) + { + try + { + _changeLog = GetStringFromResponse(result); + LatestChangeLog = GetLastestChangeLog(_changeLog); + LatestVersionNumber = GetLastestVersionNumber(LatestChangeLog); + } + catch (Exception exception) + { + if (Error == null) + { + Error = exception.Message; + } + } + } + + private static string GetLastestVersionNumber(string latestChangeLog) + { + foreach (string line in latestChangeLog.Replace(Environment.NewLine, "\n").Split('\n')) + { + string s = line.Trim(); + if (!s.Contains("BETA", StringComparison.OrdinalIgnoreCase) && !s.Contains('x') && !s.Contains('*') && s.Contains('(') && s.Contains(')') && regex.IsMatch(s)) + { + int indexOfSpace = s.IndexOf(' '); + if (indexOfSpace > 0) + return s.Substring(0, indexOfSpace).Trim(); + } + } + return null; + } + + private static string GetLastestChangeLog(string changeLog) + { + bool releaseOn = false; + var sb = new StringBuilder(); + foreach (string line in changeLog.Replace(Environment.NewLine, "\n").Split('\n')) + { + string s = line.Trim(); + if (s.Length == 0 && releaseOn) + return sb.ToString(); + + if (!releaseOn) + { + if (!s.Contains('x') && !s.Contains('*') && s.Contains('(') && s.Contains(')') && regex.IsMatch(s)) + releaseOn = true; + } + + if (releaseOn) + { + sb.AppendLine(line); + } + } + return sb.ToString(); + } + + private string GetStringFromResponse(IAsyncResult result) + { + HttpWebResponse response = (result.AsyncState as HttpWebRequest).EndGetResponse(result) as HttpWebResponse; + System.IO.Stream responseStream = response.GetResponseStream(); + byte[] buffer = new byte[5000000]; + int count = 1; + int index = 0; + while (count > 0) + { + count = responseStream.Read(buffer, index, 2048); + index += count; + } + if (index > 0) + _successCount++; + return Encoding.UTF8.GetString(buffer, 0, index); + } + + public CheckForUpdatesHelper() + { + Error = null; + _successCount = 0; + } + + public void CheckForUpdates() + { + // load github release json + //StartDownloadString(ReleasesUrl, "application/json", FinishWebRequestReleases); + + // load change log + StartDownloadString(ChangeLogUrl, null, FinishWebRequestChangeLog); + } + + public bool IsUpdateAvailable() + { + try + { + //string[] currentVersionInfo = "3.3.14".Split('.'); // for testing... + string[] currentVersionInfo = Utilities.AssemblyVersion.Split('.'); + string minorMinorVersion = string.Empty; + if (currentVersionInfo.Length >= 3 && currentVersionInfo[2] != "0") + minorMinorVersion = "." + currentVersionInfo[2]; + string currentVersion = String.Format("{0}.{1}{2}", currentVersionInfo[0], currentVersionInfo[1], minorMinorVersion); + if (currentVersion == LatestVersionNumber) + return false; + + string[] latestVersionInfo = LatestVersionNumber.Split('.'); + if (int.Parse(latestVersionInfo[0]) > int.Parse(currentVersionInfo[0])) + return true; + if (int.Parse(latestVersionInfo[0]) == int.Parse(currentVersionInfo[0]) && int.Parse(latestVersionInfo[1]) > int.Parse(currentVersionInfo[1])) + return true; + if (int.Parse(latestVersionInfo[0]) == int.Parse(currentVersionInfo[0]) && int.Parse(latestVersionInfo[1]) == int.Parse(currentVersionInfo[1]) && int.Parse(latestVersionInfo[2]) > int.Parse(currentVersionInfo[2])) + return true; + + return false; + } + catch + { + return false; + } + } + + } +} \ No newline at end of file diff --git a/libse/Forms/FixCommonErrorsHelper.cs b/libse/Forms/FixCommonErrorsHelper.cs new file mode 100644 index 000000000..96321ad16 --- /dev/null +++ b/libse/Forms/FixCommonErrorsHelper.cs @@ -0,0 +1,464 @@ +using System; +using System.Linq; + +namespace Nikse.SubtitleEdit.Core.Forms +{ + public static class FixCommonErrorsHelper + { + public static string FixEllipsesStartHelper(string text) + { + if (string.IsNullOrEmpty(text) || text.Trim().Length < 4 || !(text.Contains("..", StringComparison.Ordinal) || text.Contains(". .", StringComparison.Ordinal))) + return text; + + var pre = string.Empty; + if (text.StartsWith("', 5) >= 0) + { + var idx = text.IndexOf('>', 5); + if (idx >= 0) + { + pre = text.Substring(0, text.IndexOf('>') + 1); + text = text.Substring(idx + 1).TrimStart(); + } + } + + if (text.StartsWith("...", StringComparison.Ordinal)) + { + text = text.TrimStart('.').TrimStart(); + } + + // "...foobar" / "... foobar" / ". .. foobar" + if (text.StartsWith("\"") && (text.StartsWith("\"..") || text.StartsWith("\". .") || text.StartsWith("\" ..") || text.StartsWith("\" . ."))) + { + int removeLength = 0; + while (removeLength + 1 < text.Length && (text[1 + removeLength] == '.' || text[1 + removeLength] == ' ')) + removeLength++; + text = text.Remove(1, removeLength); + } + + text = text.Replace("-..", "- .."); + var tag = "- ..."; + if (text.StartsWith(tag, StringComparison.Ordinal)) + { + text = "- " + text.Substring(tag.Length); + while (text.StartsWith("- .", StringComparison.Ordinal)) + { + text = "- " + text.Substring(3); + text = text.Replace(" ", " "); + } + } + + tag = "..."; + if (text.StartsWith(tag, StringComparison.Ordinal)) + { + text = "" + text.Substring(tag.Length); + while (text.StartsWith(".", StringComparison.Ordinal) || text.StartsWith(" ", StringComparison.Ordinal)) + text = "" + text.Substring(4); + } + tag = " ..."; + if (text.StartsWith(tag, StringComparison.Ordinal)) + { + text = "" + text.Substring(tag.Length); + while (text.StartsWith(".", StringComparison.Ordinal) || text.StartsWith(" ", StringComparison.Ordinal)) + text = "" + text.Substring(4, text.Length - 4); + } + + tag = "- ..."; + if (text.StartsWith(tag, StringComparison.Ordinal)) + { + text = "- " + text.Substring(tag.Length); + while (text.StartsWith("- .", StringComparison.Ordinal)) + text = "- " + text.Substring(6); + } + tag = "- ..."; + if (text.StartsWith(tag, StringComparison.Ordinal)) + { + text = "- " + text.Substring(tag.Length); + while (text.StartsWith("- .", StringComparison.Ordinal)) + text = "- " + text.Substring(6); + } + + // Narrator:... Hello foo! + text = text.Replace(":..", ": .."); + tag = ": .."; + if (text.Contains(tag, StringComparison.Ordinal)) + { + text = text.Replace(": ..", ": "); + while (text.Contains(": .")) + text = text.Replace(": .", ": "); + } + + // - ... Foo + tag = "- ..."; + if (text.StartsWith(tag, StringComparison.Ordinal)) + { + text = text.Substring(tag.Length); + text = text.TrimStart('.', ' '); + text = "- " + text; + } + text = text.Replace(" ", " "); + + // WOMAN 2: ...24 hours a day at BabyC. + var index = text.IndexOf(':'); + if (index > 0 && text.Length > index + 2 && !char.IsDigit(text[index + 1]) && text.Contains("..", StringComparison.Ordinal)) + { + pre += text.Substring(0, index + 1); + if (pre.Length < 2) + return text; + + text = text.Remove(0, index + 1).TrimStart(); + text = FixEllipsesStartHelper(text); + if (pre.Length > 0) + pre += " "; + } + return pre + text; + } + + public static string FixDialogsOnOneLine(string text, string language) + { + if (text.Contains(" - ") && !text.Contains(Environment.NewLine)) + { + var noTagLines = HtmlUtil.RemoveHtmlTags(text.Replace(" - ", Environment.NewLine), true).SplitToLines(); + if (noTagLines.Length == 2) + { + string part0 = noTagLines[0]; + string part1 = noTagLines[1]; + if (part0.Length > 1 && "-—!?.\")]".Contains(part0[part0.Length - 1]) && + part1.Length > 1 && ("'" + Utilities.UppercaseLetters).Contains(part1[0])) + { + text = text.Replace(" - ", Environment.NewLine + "- "); + if (Utilities.AllLettersAndNumbers.Contains(part0[0])) + { + if (text.Length > 3 && text[0] == '<' && text[2] == '>') + text = "<" + text[1] + ">" + "- " + text.Substring(3).TrimStart(); + else + text = "- " + text; + } + } + } + } + + var stringArray = new string[] { ". -", "! -", "? -", "— -", "-- -", ") -", "] -", "> -" }; + var idx = text.IndexOfAny(stringArray, StringComparison.Ordinal); + if (idx >= 0) + { + if (Utilities.GetNumberOfLines(text) == 2) + { + string temp = Utilities.AutoBreakLine(text, 99, 33, language); + var arr = text.SplitToLines(); + var arrTemp = temp.SplitToLines(); + if (arrTemp.Length == 2 && arr.Length == 2) + { + var secLine = HtmlUtil.RemoveHtmlTags(arr[1]).TrimStart(); + var secLineTemp = HtmlUtil.RemoveHtmlTags(arrTemp[1]).TrimStart(); + if (secLineTemp.StartsWith('-') && !secLine.StartsWith('-')) + text = temp; + } + } + else if (Utilities.GetNumberOfLines(text) == 1) + { + string temp = Utilities.AutoBreakLine(text, language); + var arrTemp = temp.SplitToLines(); + if (arrTemp.Length == 2) + { + var secLineTemp = HtmlUtil.RemoveHtmlTags(arrTemp[1]).TrimStart(); + if (secLineTemp.StartsWith('-')) + text = temp; + } + else + { + int index = text.IndexOfAny(new[] { ". -", "! -", "? -", "— -" }, StringComparison.Ordinal); + if (index < 0 && text.IndexOf("-- -", StringComparison.Ordinal) > 0) + index = text.IndexOf("-- -", StringComparison.Ordinal) + 1; + if (index > 0) + { + text = text.Remove(index + 1, 1).Insert(index + 1, Environment.NewLine); + text = text.Replace(Environment.NewLine + " ", Environment.NewLine); + text = text.Replace(" " + Environment.NewLine, Environment.NewLine); + } + } + } + } + return text; + } + + public static bool IsPreviousTextEndOfParagraph(string prevText) + { + if (string.IsNullOrEmpty(prevText) || prevText.Length < 3) + return true; + + prevText = prevText.Replace("♪", string.Empty).Replace("♫", string.Empty).Trim(); + bool isPrevEndOfLine = prevText.Length > 1 && + !prevText.EndsWith("...", StringComparison.Ordinal) && + (".!?—".Contains(prevText[prevText.Length - 1]) || // em dash, unicode character + prevText.EndsWith("--", StringComparison.Ordinal)); + + if (isPrevEndOfLine && prevText.Length > 5 && prevText.EndsWith('.') && + prevText[prevText.Length - 3] == '.' && + Utilities.AllLetters.Contains(prevText[prevText.Length - 2])) + isPrevEndOfLine = false; + return isPrevEndOfLine; + } + + public static string FixHyphensRemove(Subtitle subtitle, int i) + { + Paragraph p = subtitle.Paragraphs[i]; + string text = p.Text; + + if (text.TrimStart().StartsWith('-') || + text.TrimStart().StartsWith("-", StringComparison.OrdinalIgnoreCase) || + text.TrimStart().StartsWith(" -", StringComparison.OrdinalIgnoreCase) || + text.Contains(Environment.NewLine + '-') || + text.Contains(Environment.NewLine + " -") || + text.Contains(Environment.NewLine + "-") || + text.Contains(Environment.NewLine + " -") || + text.Contains(Environment.NewLine + "-") || + text.Contains(Environment.NewLine + " -")) + { + var prev = subtitle.GetParagraphOrDefault(i - 1); + + if (prev == null || !HtmlUtil.RemoveHtmlTags(prev.Text).TrimEnd().EndsWith('-') || HtmlUtil.RemoveHtmlTags(prev.Text).TrimEnd().EndsWith("--", StringComparison.Ordinal)) + { + var noTaglines = HtmlUtil.RemoveHtmlTags(p.Text).SplitToLines(); + int startHyphenCount = noTaglines.Count(line => line.TrimStart().StartsWith('-')); + if (startHyphenCount == 1) + { + bool remove = true; + var noTagparts = HtmlUtil.RemoveHtmlTags(text).SplitToLines(); + if (noTagparts.Length == 2) + { + if (noTagparts[0].TrimStart().StartsWith('-') && noTagparts[1].Contains(": ")) + remove = false; + if (noTagparts[1].TrimStart().StartsWith('-') && noTagparts[0].Contains(": ")) + remove = false; + } + + if (remove) + { + int idx = text.IndexOf('-'); + var st = new StripableText(text); + if (idx < 5 && st.Pre.Length >= idx) + { + text = text.Remove(idx, 1).TrimStart(); + idx = text.IndexOf('-'); + st = new StripableText(text); + if (idx < 5 && idx >= 0 && st.Pre.Length >= idx) + { + text = text.Remove(idx, 1).TrimStart(); + st = new StripableText(text); + } + idx = text.IndexOf('-'); + if (idx < 5 && idx >= 0 && st.Pre.Length >= idx) + text = text.Remove(idx, 1).TrimStart(); + + text = RemoveSpacesBeginLine(text); + } + else + { + int indexOfNewLine = text.IndexOf(Environment.NewLine, StringComparison.Ordinal); + if (indexOfNewLine > 0) + { + idx = text.IndexOf('-', indexOfNewLine); + if (idx >= 0 && indexOfNewLine + 5 > indexOfNewLine) + { + text = text.Remove(idx, 1).TrimStart().Replace(Environment.NewLine + " ", Environment.NewLine); + + idx = text.IndexOf('-', indexOfNewLine); + if (idx >= 0 && indexOfNewLine + 5 > indexOfNewLine) + { + text = text.Remove(idx, 1).TrimStart(); + + text = RemoveSpacesBeginLine(text); + } + } + } + } + } + } + } + } + else if (text.StartsWith(" ", Environment.NewLine + ""); + text = text.Replace(Environment.NewLine + " ", Environment.NewLine + ""); + text = text.Replace(Environment.NewLine + " ", Environment.NewLine + ""); + + if (text.LineStartsWithHtmlTag(true) && text[3] == 0x20) + { + text = text.Remove(3, 1).TrimStart(); + } + if (text.LineStartsWithHtmlTag(false, true)) + { + var closeIdx = text.IndexOf('>'); + if (closeIdx > 6 && text[closeIdx + 1] == 0x20) + text = text.Remove(closeIdx + 1, 1); + } + return text; + } + + public static string RemoveSpacesBeginLineAfterEllipses(string line) + { + if (line.StartsWith("... ", StringComparison.Ordinal)) + line = line.Remove(3, 1); + if (line.Length > 6 && line.LineStartsWithHtmlTag(true)) // ... foobar + { + var idx = line.IndexOf('>') + 1; + var pre = line.Substring(0, idx); + line = line.Remove(0, idx).TrimStart(); + if (line.StartsWith("... ", StringComparison.Ordinal)) + line = line.Remove(3, 1); + line = pre + line; + } + if (line.LineStartsWithHtmlTag(false, true)) // and + { + var closeIdx = line.IndexOf('>', 5); + if (closeIdx >= 5 && line.Length > closeIdx + 5) + { + var fontTag = line.Substring(0, closeIdx + 1).TrimStart(); + line = line.Substring(closeIdx + 1).TrimStart(); + if (line.StartsWith("... ", StringComparison.Ordinal)) + line = line.Remove("... ".Length - 1, 1); + line = fontTag + line; + } + } + return line; + } + + public static string FixHyphensAdd(Subtitle subtitle, int i, string language) + { + Paragraph p = subtitle.Paragraphs[i]; + string text = p.Text; + var textCache = HtmlUtil.RemoveHtmlTags(text.TrimStart()); + if (textCache.StartsWith('-') || textCache.Contains(Environment.NewLine + "-")) + { + Paragraph prev = subtitle.GetParagraphOrDefault(i - 1); + + if (prev == null || !HtmlUtil.RemoveHtmlTags(prev.Text).TrimEnd().EndsWith('-') || HtmlUtil.RemoveHtmlTags(prev.Text).TrimEnd().EndsWith("--", StringComparison.Ordinal)) + { + var lines = HtmlUtil.RemoveHtmlTags(p.Text).SplitToLines(); + int startHyphenCount = lines.Count(line => line.TrimStart().StartsWith('-')); + int totalSpaceHyphen = Utilities.CountTagInText(text, " -"); + if (startHyphenCount == 1 && totalSpaceHyphen == 0) + { + var parts = HtmlUtil.RemoveHtmlTags(text).SplitToLines(); + if (parts.Length == 2) + { + var part0 = parts[0].TrimEnd(); + bool doAdd = "!?.".Contains(part0[part0.Length - 1]) || language == "ko"; + if (parts[0].TrimStart().StartsWith('-') && parts[1].Contains(':') && !doAdd) + doAdd = false; + if (parts[1].TrimStart().StartsWith('-') && parts[0].Contains(':') && !doAdd) + doAdd = false; + + if (doAdd) + { + int idx = text.IndexOf('-'); + int newLineIdx = text.IndexOf(Environment.NewLine, StringComparison.Ordinal); + bool addSecondLine = idx < newLineIdx ? true : false; + + if (addSecondLine && idx > 0 && Utilities.AllLetters.Contains(text[idx - 1])) + addSecondLine = false; + if (addSecondLine) + { + // add dash in second line. + newLineIdx += 2; + if (text.LineBreakStartsWithHtmlTag(true)) + { + text = text.Insert(newLineIdx + 3, "- ").TrimEnd(); + } + else + { + text = text.Replace(Environment.NewLine, Environment.NewLine + "- ").Replace(Environment.NewLine + "- ", Environment.NewLine + "- "); + } + } + else + { + // add dash in first line. + if (text.LineStartsWithHtmlTag(true)) + text = text.Substring(0, 3) + "- " + text.Remove(0, 3).TrimEnd(); + else if (text.StartsWith("{\\an", StringComparison.Ordinal) && text.Length > 6 && text[5] == '}') + text = text.Insert(6, "- "); + else + text = "- " + text.Trim(); + } + } + } + } + // - Shut it off. -Get the fuck
out of here, Darryl. + if (totalSpaceHyphen == 1 && startHyphenCount == 1) + { + var idx = text.IndexOf(" -", StringComparison.Ordinal); + if (idx > 1 && ".?!".Contains(text[idx - 1]) && idx + 2 < text.Length) + { + var firstLine = text.Substring(0, idx).Replace(Environment.NewLine, " ").Trim(); + var secondLine = text.Substring(idx + 1).Insert(1, " ").Replace(Environment.NewLine, " ").Trim(); + text = firstLine + Environment.NewLine + secondLine; + } + } + } + } + return text; + } + + public static string FixDoubleGreaterThanHelper(string text) + { + string post = string.Empty; + if (text.Length > 3 && text[0] == '<' && text[2] == '>' && (text[1] == 'i' || text[1] == 'b' || text[1] == 'u')) + { + post += "<" + text[1] + ">"; + text = text.Remove(0, 3).TrimStart(); + } + if (text.StartsWith("', 5); + if (endIdx >= 5 && endIdx < text.Length - 7) + { + post += text.Substring(0, endIdx + 1); + text = text.Substring(endIdx + 1).TrimStart(); + } + } + if (text.StartsWith(">>", StringComparison.Ordinal) && text.Length > 3) + text = text.TrimStart('>', ' ').TrimStart(); + return post + text; + } + + public static string FixShortLines(string text) + { + if (string.IsNullOrWhiteSpace(text) || !text.Contains(Environment.NewLine, StringComparison.Ordinal)) + return text; + + string s = HtmlUtil.RemoveHtmlTags(text, true); + if (s.Contains(Environment.NewLine) && s.Replace(Environment.NewLine, " ").Replace(" ", " ").Length < Configuration.Settings.Tools.MergeLinesShorterThan) + { + s = s.TrimEnd().TrimEnd('.', '?', '!', ':', ';'); + s = s.TrimStart('-'); + if (!s.Contains(new[] { '.', '?', '!', ':', ';', '-', '♪', '♫' }) && + !(s.StartsWith('[') && s.Contains("]" + Environment.NewLine, StringComparison.Ordinal)) && + !(s.StartsWith('(') && s.Contains(")" + Environment.NewLine, StringComparison.Ordinal)) && + s != s.ToUpper()) + { + return Utilities.UnbreakLine(text); + } + } + return text; + } + + } +} \ No newline at end of file diff --git a/libse/Forms/RemoveTextForHI.cs b/libse/Forms/RemoveTextForHI.cs new file mode 100644 index 000000000..5cd21418a --- /dev/null +++ b/libse/Forms/RemoveTextForHI.cs @@ -0,0 +1,1122 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.Forms +{ + public class RemoveTextForHI + { + public RemoveTextForHISettings Settings { get; set; } + + public List Warnings; + public int WarningIndex; + private List _interjectionList; + + public RemoveTextForHI(RemoveTextForHISettings removeTextForHISettings) + { + Settings = removeTextForHISettings; + } + + public void ResetInterjections() + { + _interjectionList = null; + } + + public string RemoveHearImpairedtagsInsideLine(string newText) + { + for (int i = 6; i < newText.Length; i++) + { + var s = newText.Substring(i); + if (s.Length > 2 && (s.StartsWith('.') || s.StartsWith('!') || s.StartsWith('?'))) + { + var pre = string.Empty; + + s = s.Remove(0, 1); + if (s.StartsWith(' ')) + { + if (s.StartsWith(" ", StringComparison.Ordinal)) + pre = " "; + else + pre = " "; + } + else if (s.StartsWith("", StringComparison.Ordinal)) + pre = ""; + else if (s.StartsWith("", StringComparison.Ordinal)) + pre = ""; + + if (pre.Length > 0) + { + s = s.Remove(0, pre.Length); + if (s.StartsWith(' ') && s.Length > 1) + { + pre += " "; + s = s.Remove(0, 1); + } + + if (HasHearImpariedTagsAtStart(s)) + { + s = RemoveStartEndTags(s); + newText = newText.Substring(0, i + 1) + pre + " " + s; + newText = newText.Replace("", string.Empty); + newText = newText.Replace(" ", " ").FixExtraSpaces(); + } + } + } + } + return newText; + } + + public string RemoveColon(string text) + { + if (!Settings.RemoveTextBeforeColon || text.IndexOf(':') < 0) + return text; + + string preAssTag = string.Empty; + if (text.StartsWith("{\\", StringComparison.Ordinal) && text.IndexOf('}') > 0) + { + int indexOfEndBracket = text.IndexOf('}') + 1; + preAssTag = text.Substring(0, indexOfEndBracket); + text = text.Remove(0, indexOfEndBracket).TrimStart(); + } + + // House 7x01 line 52: and she would like you to do three things: + // Okay or remove??? + var noTagText = HtmlUtil.RemoveHtmlTags(text); + if (noTagText.IndexOf(':') > 0 && noTagText.IndexOf(':') == noTagText.Length - 1 && noTagText != noTagText.ToUpper() && noTagText.Length > 10) + return text; + + string newText = string.Empty; + var lines = text.Trim().SplitToLines(); + int noOfNames = 0; + int count = 0; + bool removedInFirstLine = false; + bool removedInSecondLine = false; + foreach (string line in lines) + { + int indexOfColon = line.IndexOf(':'); + if (indexOfColon > 0 && IsNotInsideBrackets(line, indexOfColon)) + { + var pre = line.Substring(0, indexOfColon); + var noTagPre = HtmlUtil.RemoveHtmlTags(pre, true); + if (Settings.RemoveTextBeforeColonOnlyUppercase && noTagPre != noTagPre.ToUpper()) + { + newText = (newText + Environment.NewLine + line).Trim(); + } + else + { + var st = new StripableText(pre); + if (count == 1 && newText.Length > 1 && removedInFirstLine && Utilities.CountTagInText(line, ':') == 1 && + ".?!".IndexOf(newText[newText.Length - 1]) < 0 && newText.LineEndsWithHtmlTag(true) && + line != line.ToUpper()) + { + if (pre.Contains("") && line.Contains("")) + newText = newText + Environment.NewLine + "" + line; + else if (pre.Contains("") && line.Contains("")) + newText = newText + Environment.NewLine + "" + line; + else if (pre.Contains("") && line.Contains("")) + newText = newText + Environment.NewLine + "" + line; + else if (pre.Contains('[') && line.Contains(']')) + newText = newText + Environment.NewLine + "[" + line; + else if (pre.Contains('(') && line.EndsWith(')')) + newText = newText + Environment.NewLine + "(" + line; + else + newText = newText + Environment.NewLine + line; + } + else if (count == 1 && newText.Length > 1 && indexOfColon > 15 && line.Substring(0, indexOfColon).Contains(' ') && Utilities.CountTagInText(line, ':') == 1 && + ".?!".IndexOf(newText[newText.Length - 1]) < 0 && newText.LineEndsWithHtmlTag(true) && + line != line.ToUpper()) + { + if (pre.Contains("") && line.Contains("")) + newText = newText + Environment.NewLine + "" + line; + else if (pre.Contains("") && line.Contains("")) + newText = newText + Environment.NewLine + "" + line; + else if (pre.Contains("") && line.Contains("")) + newText = newText + Environment.NewLine + "" + line; + else if (pre.Contains('[') && line.Contains(']')) + newText = newText + Environment.NewLine + "[" + line; + else if (pre.Contains('(') && line.EndsWith(')')) + newText = newText + Environment.NewLine + "(" + line; + else + newText = newText + Environment.NewLine + line; + } + else if (Utilities.CountTagInText(line, ':') == 1) + { + bool remove = true; + if (indexOfColon > 0 && indexOfColon < line.Length - 1) + { + remove = !Utilities.IsBetweenNumbers(line, indexOfColon); + } + + if (!DoRemove(pre)) + remove = false; + + if (remove && Settings.ColonSeparateLine) + { + if (indexOfColon == line.Length - 1 || line.Substring(indexOfColon + 1).StartsWith(Environment.NewLine, StringComparison.Ordinal)) + remove = true; + else + remove = false; + } + + if (remove) + { + var content = line.Substring(indexOfColon + 1).Trim(); + if (content.Length > 0) + { + if (pre.Contains("") && content.Contains("")) + newText = newText + Environment.NewLine + "" + content; + else if (pre.Contains("") && content.Contains("")) + newText = newText + Environment.NewLine + "" + content; + else if (pre.Contains('[') && content.Contains(']')) + newText = newText + Environment.NewLine + "[" + content; + else if (pre.Contains('(') && content.EndsWith(')')) + newText = newText + Environment.NewLine + "(" + content; + else + newText = newText + Environment.NewLine + content; + + if (count == 0) + removedInFirstLine = true; + else if (count == 1) + removedInSecondLine = true; + } + newText = newText.Trim(); + + if (text.StartsWith('(') && newText.EndsWith(')') && !newText.Contains('(')) + newText = newText.TrimEnd(')'); + else if (text.StartsWith('[') && newText.EndsWith(']') && !newText.Contains('[')) + newText = newText.TrimEnd(']'); + else if (newText.EndsWith("", StringComparison.Ordinal) && text.StartsWith("", StringComparison.Ordinal) && !newText.StartsWith("", StringComparison.Ordinal)) + newText = "" + newText; + else if (newText.EndsWith("", StringComparison.Ordinal) && text.StartsWith("", StringComparison.Ordinal) && !newText.StartsWith("", StringComparison.Ordinal)) + newText = "" + newText; + else if (newText.EndsWith("", StringComparison.Ordinal) && text.StartsWith("", StringComparison.Ordinal) && !newText.StartsWith("", StringComparison.Ordinal)) + newText = "" + newText; + + if (!IsHIDescription(st.StrippedText)) + noOfNames++; + } + else + { + newText = (newText + Environment.NewLine + line).Trim(); + if (newText.EndsWith("", StringComparison.Ordinal) && text.StartsWith("", StringComparison.Ordinal) && !newText.StartsWith("", StringComparison.Ordinal)) + newText = "" + newText; + else if (newText.EndsWith("", StringComparison.Ordinal) && text.StartsWith("", StringComparison.Ordinal) && !newText.StartsWith("", StringComparison.Ordinal)) + newText = "" + newText; + else if ((newText.EndsWith("", StringComparison.Ordinal) && text.StartsWith("", StringComparison.Ordinal) && !newText.StartsWith("", StringComparison.Ordinal))) + newText = "" + newText; + } + } + else + { + string s2 = line; + for (int k = 0; k < 2; k++) + { + if (s2.Contains(':')) + { + int colonIndex = s2.IndexOf(':'); + string start = s2.Substring(0, colonIndex); + + if (!Settings.RemoveTextBeforeColonOnlyUppercase || start == start.ToUpper()) + { + int endIndex = start.LastIndexOfAny(new[] { '.', '!', '?' }); + if (colonIndex > 0 && colonIndex < s2.Length - 1) + { + if (char.IsDigit(s2[colonIndex - 1]) && char.IsDigit(s2[colonIndex + 1])) + endIndex = 0; + } + if (endIndex < 0) + s2 = s2.Remove(0, colonIndex - endIndex); + else if (endIndex > 0) + s2 = s2.Remove(endIndex + 1, colonIndex - endIndex); + } + + if (count == 0) + removedInFirstLine = true; + else if (count == 1) + removedInSecondLine = true; + } + } + newText = newText + Environment.NewLine + s2; + newText = newText.Trim(); + } + } + } + else + { + newText = (newText + Environment.NewLine + line).Trim(); + + if (newText.EndsWith("", StringComparison.Ordinal) && text.StartsWith("", StringComparison.Ordinal) && !newText.StartsWith("", StringComparison.Ordinal)) + newText = "" + newText; + else if (newText.EndsWith("", StringComparison.Ordinal) && text.StartsWith("", StringComparison.Ordinal) && !newText.StartsWith("", StringComparison.Ordinal)) + newText = "" + newText; + } + count++; + } + newText = newText.Trim(); + if (noOfNames > 0 && Utilities.GetNumberOfLines(newText) == 2) + { + int indexOfDialogChar = newText.IndexOf('-'); + bool insertDash = true; + var arr = newText.SplitToLines(); + if (arr.Length == 2 && arr[0].Length > 1 && arr[1].Length > 1) + { + string arr0 = new StripableText(arr[0]).StrippedText; + string arr1 = new StripableText(arr[1]).StrippedText; + + //line continuation? + if (arr0.Length > 0 && arr1.Length > 1 && (Utilities.LowercaseLetters + ",").Contains(arr0.Substring(arr0.Length - 1), StringComparison.Ordinal) && + Utilities.LowercaseLetters.Contains(arr1[0])) + { + if (new StripableText(arr[1]).Pre.Contains("...") == false) + insertDash = false; + } + + string tempArr0QuoteTrimmed = arr[0].TrimEnd('"'); + if (arr0.Length > 0 && arr1.Length > 1 && + !(tempArr0QuoteTrimmed.EndsWith('.') || tempArr0QuoteTrimmed.EndsWith('!') || tempArr0QuoteTrimmed.EndsWith('?') || tempArr0QuoteTrimmed.EndsWith("", StringComparison.Ordinal)) && + !(new StripableText(arr[1]).Pre.Contains('-'))) + { + insertDash = false; + } + + if (removedInFirstLine && !removedInSecondLine && !text.StartsWith('-') && !text.StartsWith("-", StringComparison.Ordinal)) + { + if (!insertDash || (!arr[1].StartsWith('-') && !arr[1].StartsWith("-", StringComparison.Ordinal))) + insertDash = false; + } + } + + if (insertDash) + { + if (indexOfDialogChar < 0 || indexOfDialogChar > 4) + { + var st = new StripableText(newText, string.Empty, string.Empty); + newText = st.Pre + "- " + st.StrippedText + st.Post; + } + + int indexOfNewLine = newText.IndexOf(Environment.NewLine, StringComparison.Ordinal); + string second = newText.Substring(indexOfNewLine).Trim(); + indexOfDialogChar = second.IndexOf('-'); + if (indexOfDialogChar < 0 || indexOfDialogChar > 6) + { + var st = new StripableText(second, String.Empty, String.Empty); + second = st.Pre + "- " + st.StrippedText + st.Post; + newText = newText.Remove(indexOfNewLine) + Environment.NewLine + second; + } + } + } + else if (!newText.Contains(Environment.NewLine) && newText.Contains('-')) + { + var st = new StripableText(newText); + if (st.Pre.Contains('-')) + newText = st.Pre.Replace("-", string.Empty) + st.StrippedText + st.Post; + } + else if (Utilities.GetNumberOfLines(newText) == 2 && removedInFirstLine == false && removedInSecondLine) + { + string noTags = HtmlUtil.RemoveHtmlTags(newText, true).Trim(); + bool insertDash = noTags.StartsWith('-') && Utilities.CountTagInText(noTags, '-') == 1; + if (insertDash) + { + if (newText.Contains(Environment.NewLine + "")) + newText = newText.Replace(Environment.NewLine + "", Environment.NewLine + "- "); + else + newText = newText.Replace(Environment.NewLine, Environment.NewLine + "- "); + } + } + if (text.Contains("", StringComparison.Ordinal) && !newText.Contains("", StringComparison.Ordinal) && newText.EndsWith("", StringComparison.Ordinal)) + newText = "" + newText; + + if (string.IsNullOrWhiteSpace(newText)) + return string.Empty; + + return preAssTag + newText; + } + + private bool IsNotInsideBrackets(string text, int colonIdx) + { + // ♪ (THE CAPITOLS: "COOL JERK") ♪ + var bIdx = text.IndexOfAny(new[] { '(', '[' }, 0, colonIdx); + if (bIdx >= 0) + { + char closeType = text[bIdx] == '(' ? ')' : ']'; + var nIdx = text.IndexOf(closeType, bIdx + 1); + if (nIdx > colonIdx) + { + return false; + } + } + return true; + } + + private bool DoRemove(string pre) + { + // Skip these: Barry, remember: She cannot; http://google.com; Improved by: ... + if (pre.IndexOfAny(new[] { "Previously on", "Improved by", "http", ", " }, StringComparison.OrdinalIgnoreCase) >= 0) + return false; + + // Okay! Narrator: Hello! + if (pre.IndexOfAny(new[] { '!', '?' }) > 0) + return false; + + return true; + } + + public string RemoveTextFromHearImpaired(string text) + { + if (Settings.RemoveWhereContains) + { + foreach (var removeIfTextContain in Settings.RemoveIfTextContains) + { + if (text.Contains(removeIfTextContain)) + return string.Empty; + } + } + + string oldText = text; + text = RemoveColon(text); + string pre = " >-\"'‘`´♪¿¡.…—"; + string post = " -\"'`´♪.!?:…—"; + if (Settings.RemoveTextBetweenCustomTags) + { + pre = pre.Replace(Settings.CustomStart, string.Empty); + post = post.Replace(Settings.CustomEnd, string.Empty); + } + var st = new StripableText(text, pre, post); + var sb = new StringBuilder(); + var parts = st.StrippedText.Trim().SplitToLines(); + int lineNumber = 0; + bool removedDialogInFirstLine = false; + int noOfNamesRemoved = 0; + int noOfNamesRemovedNotInLineOne = 0; + foreach (string s in parts) + { + var stSub = new StripableText(s, pre, post); + string tempStrippedtext = stSub.StrippedText; + if (lineNumber == parts.Length - 1 && st.Post.Contains('?')) + tempStrippedtext += "?"; + else if (stSub.Post.Contains('?')) + tempStrippedtext += "?"; + if (!StartAndEndsWithHearImpariedTags(tempStrippedtext)) + { + if (removedDialogInFirstLine && stSub.Pre.Contains("- ", StringComparison.Ordinal)) + stSub.Pre = stSub.Pre.Replace("- ", string.Empty); + + string newText = stSub.StrippedText; + + newText = RemoveHearImpairedTags(newText); + + if (stSub.StrippedText.Length - newText.Length > 2) + { + string removedText = GetRemovedString(stSub.StrippedText, newText); + if (!IsHIDescription(removedText)) + { + noOfNamesRemoved++; + if (lineNumber > 0) + noOfNamesRemovedNotInLineOne++; + } + } + sb.AppendLine(stSub.Pre + newText + stSub.Post); + } + else + { + if (!IsHIDescription(stSub.StrippedText)) + { + noOfNamesRemoved++; + if (lineNumber > 0) + noOfNamesRemovedNotInLineOne++; + } + + if (st.Pre.Contains("- ") && lineNumber == 0) + { + st.Pre = st.Pre.Replace("- ", string.Empty); + removedDialogInFirstLine = true; + } + else if (st.Pre == "-" && lineNumber == 0) + { + st.Pre = string.Empty; + removedDialogInFirstLine = true; + } + + if (st.Pre.Contains("") && stSub.Post.Contains("")) + st.Pre = st.Pre.Replace("", string.Empty); + + if (s.Contains("") && !s.Contains("") && st.Post.Contains("")) + st.Post = st.Post.Replace("", string.Empty); + } + lineNumber++; + } + + text = st.Pre + sb.ToString().Trim() + st.Post; + text = text.Replace(" ", " ").Trim(); + text = text.Replace("", string.Empty); + text = text.Replace(" ", " "); + text = text.Replace("", string.Empty); + text = text.Replace(" ", " "); + text = text.Replace("", string.Empty); + text = text.Replace(" ", " "); + text = RemoveEmptyFontTag(text); + text = text.Replace(" ", " ").Trim(); + text = RemoveColon(text); + text = RemoveLineIfAllUppercase(text); + text = RemoveHearImpairedtagsInsideLine(text); + if (Settings.RemoveInterjections) + text = RemoveInterjections(text); + + st = new StripableText(text, " >-\"'‘`´♪¿¡.…—", " -\"'`´♪.!?:…—"); + text = st.StrippedText; + if (StartAndEndsWithHearImpariedTags(text)) + { + text = RemoveStartEndTags(text); + } + + text = RemoveHearImpairedTags(text); + + // fix 3 lines to two liners - if only two lines + if (noOfNamesRemoved >= 1 && Utilities.GetNumberOfLines(text) == 3) + { + char[] chars = { '!', '?', '.' }; + string[] a = HtmlUtil.RemoveHtmlTags(text).Replace(" ", string.Empty).Split(chars, StringSplitOptions.RemoveEmptyEntries); + if (a.Length == 2) + { + var temp = new StripableText(text); + temp.StrippedText = temp.StrippedText.Replace(Environment.NewLine, " "); + int splitIndex = temp.StrippedText.LastIndexOfAny(chars); + if (splitIndex > 0) + { + text = temp.Pre + temp.StrippedText.Insert(splitIndex + 1, Environment.NewLine) + temp.Post; + } + } + } + + if (!text.StartsWith('-') && noOfNamesRemoved >= 1 && Utilities.GetNumberOfLines(text) == 2) + { + string[] arr = text.SplitToLines(); + string part0 = arr[0].Trim().Replace("", string.Empty).Trim(); + if (!part0.EndsWith(',') && (!part0.EndsWith('-') || noOfNamesRemovedNotInLineOne > 0)) + { + if (part0.Length > 0 && @".!?".Contains(part0[part0.Length - 1])) + { + if (noOfNamesRemovedNotInLineOne > 0) + { + if (!st.Pre.Contains('-')) + text = "- " + text.Replace(Environment.NewLine, Environment.NewLine + "- "); + if (!text.Contains(Environment.NewLine + "-") && !text.Contains(Environment.NewLine + "-")) + text = text.Replace(Environment.NewLine, Environment.NewLine + "- "); + } + } + } + } + + if (!string.IsNullOrEmpty(text) || (st.Pre.Contains('♪') || st.Post.Contains('♪'))) + text = st.Pre + text + st.Post; + + if (oldText.TrimStart().StartsWith("- ", StringComparison.Ordinal) && text != null && !text.Contains(Environment.NewLine) && + (oldText.Contains(Environment.NewLine + "- ", StringComparison.Ordinal) || + oldText.Contains(Environment.NewLine + " - ", StringComparison.Ordinal) || + oldText.Contains(Environment.NewLine + "- ", StringComparison.Ordinal) || + oldText.Contains(Environment.NewLine + " - ", StringComparison.Ordinal))) + { + text = text.TrimStart().TrimStart('-').TrimStart(); + } + if (oldText.TrimStart().StartsWith("-", StringComparison.Ordinal) && + !oldText.TrimStart().StartsWith("--", StringComparison.Ordinal) && + text != null && !text.Contains(Environment.NewLine) && + (oldText.Contains(Environment.NewLine + "-", StringComparison.Ordinal) && !oldText.Contains(Environment.NewLine + "--", StringComparison.Ordinal) || + oldText.Contains(Environment.NewLine + " - ", StringComparison.Ordinal) || + oldText.Contains(Environment.NewLine + "- ", StringComparison.Ordinal) || + oldText.Contains(Environment.NewLine + " - ", StringComparison.Ordinal))) + { + text = text.TrimStart().TrimStart('-').TrimStart(); + } + + if (oldText.TrimStart().StartsWith("- ", StringComparison.Ordinal) && + text != null && text.StartsWith("- ", StringComparison.Ordinal) && !text.Contains(Environment.NewLine, StringComparison.Ordinal) && + (oldText.Contains(Environment.NewLine + "- ", StringComparison.Ordinal) || + oldText.Contains(Environment.NewLine + " - ", StringComparison.Ordinal) || + oldText.Contains(Environment.NewLine + "- ", StringComparison.Ordinal) || + oldText.Contains(Environment.NewLine + " - ", StringComparison.Ordinal))) + { + text = text.Remove(3, 2); + } + + if (text != null && !text.Contains(Environment.NewLine, StringComparison.Ordinal) && + (oldText.Contains(':') && !text.Contains(':') || + oldText.Contains('[') && !text.Contains('[') || + oldText.Contains('(') && !text.Contains('(') || + oldText.Contains('{') && !text.Contains('{')) && + (oldText.Contains(Environment.NewLine + "- ", StringComparison.Ordinal) || + oldText.Contains(Environment.NewLine + " - ", StringComparison.Ordinal) || + oldText.Contains(Environment.NewLine + "- ", StringComparison.Ordinal) || + oldText.Contains(Environment.NewLine + " - ", StringComparison.Ordinal))) + { + text = text.TrimStart().TrimStart('-').TrimStart(); + } + + if (oldText != text) + { + // insert spaces before "-" + text = text.Replace(Environment.NewLine + "- ", Environment.NewLine + "- "); + text = text.Replace(Environment.NewLine + "-", Environment.NewLine + "- "); + if (text.StartsWith('-') && text.Length > 2 && text[1] != ' ' && text[1] != '-') + text = text.Insert(1, " "); + if (text.StartsWith("-", StringComparison.Ordinal) && text.Length > 5 && text[4] != ' ' && text[4] != '-') + text = text.Insert(4, " "); + if (text.Contains(Environment.NewLine + "-", StringComparison.Ordinal)) + { + int index = text.IndexOf(Environment.NewLine + "-", StringComparison.Ordinal); + if (index + 4 < text.Length && text[index + Environment.NewLine.Length + 1] != ' ' && text[index + Environment.NewLine.Length + 1] != '-') + text = text.Insert(index + Environment.NewLine.Length + 1, " "); + } + if (text.Contains(Environment.NewLine + "-", StringComparison.Ordinal)) + { + int index = text.IndexOf(Environment.NewLine + "-", StringComparison.Ordinal); + if (index + 5 < text.Length && text[index + Environment.NewLine.Length + 4] != ' ' && text[index + Environment.NewLine.Length + 4] != '-') + text = text.Insert(index + Environment.NewLine.Length + 4, " "); + } + } + return text.Trim(); + } + + private string RemoveEmptyFontTag(string text) + { + int indexOfStartFont = text.IndexOf("", StringComparison.OrdinalIgnoreCase); + if (indexOfEndFont > 0 && indexOfStartFont < indexOfEndFont) + { + int startTagBefore = text.Substring(0, indexOfEndFont).LastIndexOf('<'); + string lastTwo = text.Substring(indexOfEndFont - 2, 2); + if (startTagBefore == indexOfStartFont && lastTwo.TrimEnd().EndsWith('>')) + { + text = text.Remove(indexOfStartFont, indexOfEndFont + "".Length - indexOfStartFont); + if (lastTwo.EndsWith(' ')) + text = text.Insert(indexOfStartFont, " "); + text = text.Replace(" ", " "); + } + } + return text; + } + + private void AddWarning() + { + if (Warnings == null || WarningIndex < 0) + return; + + Warnings.Add(WarningIndex); + } + + private bool IsHIDescription(string text) + { + text = text.Trim(' ', '(', ')', '[', ']', '?', '{', '}'); + text = text.ToLower(); + + if (text.Trim().Replace("mr. ", string.Empty).Replace("mrs. ", string.Empty).Replace("dr. ", string.Empty).Contains(' ')) + AddWarning(); + + if (text == "cackles" || + text == "cheers" || + text == "clears throat" || + text == "chitters" || + text == "chuckles" || + text == "exclaims" || + text == "exhales" || + text == "gasps" || + text == "grunts" || + text == "groans" || + text == "growls" || + text == "explosion" || + text == "laughs" || + text == "noise" || + text.EndsWith("on tv", StringComparison.Ordinal) || + text.StartsWith("engine ", StringComparison.Ordinal) || + text == "roars" || + text == "scoff" || + text == "screeches" || + text.EndsWith("shatters", StringComparison.Ordinal) || + text == "shouts" || + text == "shrieks" || + text == "sigh" || + text == "sighs" || + text == "snores" || + text == "stutters" || + text == "thuds" || + text == "trumpets" || + text == "whispers" || + text == "whisper" || + text == "whistles" || + text.EndsWith("ing", StringComparison.Ordinal)) + return true; + return false; + } + + private static string GetRemovedString(string oldText, string newText) + { + oldText = oldText.ToLower(); + newText = newText.ToLower(); + int start = oldText.IndexOf(newText, StringComparison.Ordinal); + string result; + if (start > 0) + result = oldText.Substring(0, oldText.Length - newText.Length); + else + result = oldText.Substring(newText.Length); + result = result.Trim(' ', '(', ')', '[', ']', '?', '{', '}'); + return result; + } + + private static int CompareLength(string a, string b) + { + return b.Length.CompareTo(a.Length); + } + + public string RemoveInterjections(string text) + { + string oldText = text; + + var arr = Configuration.Settings.Tools.Interjections.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); + if (_interjectionList == null) + { + _interjectionList = new List(); + foreach (string s in arr) + { + if (s.Length > 0) + { + if (!_interjectionList.Contains(s)) + _interjectionList.Add(s); + + string lower = s.ToLower(); + if (!_interjectionList.Contains(lower)) + _interjectionList.Add(lower); + + string upper = s.ToUpper(); + if (!_interjectionList.Contains(upper)) + _interjectionList.Add(upper); + + string pascalCasing = char.ToUpper(s[0]) + s.Substring(1); + if (!_interjectionList.Contains(pascalCasing)) + _interjectionList.Add(pascalCasing); + } + } + _interjectionList.Sort(CompareLength); + } + + bool doRepeat = true; + while (doRepeat) + { + doRepeat = false; + foreach (string s in _interjectionList) + { + if (text.Contains(s)) + { + var regex = new Regex("\\b" + s + "\\b"); + var match = regex.Match(text); + if (match.Success) + { + int index = match.Index; + string temp = text.Remove(index, s.Length); + if (temp.Remove(0, index) == " —" && temp.EndsWith("— —")) + { + temp = temp.TrimEnd('—').TrimEnd(); + if (temp.TrimEnd().EndsWith(Environment.NewLine + "—")) + temp = temp.TrimEnd().TrimEnd('—').TrimEnd(); + } + else if (temp.Remove(0, index) == " —" && temp.EndsWith("- —")) + { + temp = temp.TrimEnd('—').TrimEnd(); + if (temp.TrimEnd().EndsWith(Environment.NewLine + "-")) + temp = temp.TrimEnd().TrimEnd('-').TrimEnd(); + } + else if (index == 2 && temp.StartsWith("- —", StringComparison.Ordinal)) + { + temp = temp.Remove(2, 2); + } + else if (index == 2 && temp.StartsWith("- —", StringComparison.Ordinal)) + { + temp = temp.Remove(2, 1); + } + else if (index == 0 && temp.StartsWith(" —", StringComparison.Ordinal)) + { + temp = temp.Remove(0, 2); + } + else if (index == 0 && temp.StartsWith('—')) + { + temp = temp.Remove(0, 1); + } + string pre = string.Empty; + if (index > 0) + doRepeat = true; + + bool removeAfter = true; + + if (temp.Length > index - s.Length + 3 && index > s.Length) + { + if (temp.Substring(index - s.Length + 1, 3) == ", !") + { + temp = temp.Remove(index - s.Length + 1, 2); + removeAfter = false; + } + else if (temp.Substring(index - s.Length + 1, 3) == ", ?") + { + temp = temp.Remove(index - s.Length + 1, 2); + removeAfter = false; + } + else if (temp.Substring(index - s.Length + 1, 3) == ", .") + { + temp = temp.Remove(index - s.Length + 1, 2); + removeAfter = false; + } + } + if (removeAfter && temp.Length > index - s.Length + 2 && index > s.Length) + { + if (temp.Substring(index - s.Length, 3) == ", !") + { + temp = temp.Remove(index - s.Length, 2); + removeAfter = false; + } + else if (temp.Substring(index - s.Length, 3) == ", ?") + { + temp = temp.Remove(index - s.Length, 2); + removeAfter = false; + } + else if (temp.Substring(index - s.Length, 3) == ", .") + { + temp = temp.Remove(index - s.Length, 2); + removeAfter = false; + } + else if (index > 0 && temp.Substring(index - s.Length).StartsWith(", -—")) + { + temp = temp.Remove(index - s.Length, 3); + removeAfter = false; + } + else if (index > 0 && temp.Substring(index - s.Length).StartsWith(", --")) + { + temp = temp.Remove(index - s.Length, 2); + removeAfter = false; + } + } + if (removeAfter && temp.Length > index - s.Length + 2 && index > s.Length) + { + if (temp.Substring(index - s.Length + 1, 2) == "-!") + { + temp = temp.Remove(index - s.Length + 1, 1); + removeAfter = false; + } + else if (temp.Substring(index - s.Length + 1, 2) == "-?") + { + temp = temp.Remove(index - s.Length + 1, 1); + removeAfter = false; + } + else if (temp.Substring(index - s.Length + 1, 2) == "-.") + { + temp = temp.Remove(index - s.Length + 1, 1); + removeAfter = false; + } + } + + if (index > 3 && index - 2 < temp.Length && temp.Substring(index - 2).StartsWith(", —", StringComparison.Ordinal)) + { + temp = temp.Remove(index - 2, 1); + index--; + } + else if (index > 3 && index - 2 < temp.Length && temp.Substring(index - 2).StartsWith(", —", StringComparison.Ordinal)) + { + temp = temp.Remove(index - 2, 1); + index--; + } + + if (removeAfter) + { + if (index == 0) + { + if (!string.IsNullOrEmpty(temp) && temp.StartsWith('-')) + temp = temp.Remove(0, 1).Trim(); + } + else if (index == 3 && !string.IsNullOrEmpty(temp) && temp.StartsWith("-", StringComparison.Ordinal)) + { + temp = temp.Remove(3, 1); + } + else if (index > 0 && temp.Length > index) + { + pre = text.Substring(0, index); + temp = temp.Remove(0, index); + if (pre.EndsWith('-') && temp.StartsWith('-')) + temp = temp.Remove(0, 1); + if (pre.EndsWith("- ") && temp.StartsWith('-')) + temp = temp.Remove(0, 1); + } + + while (temp.Length > 0 && (temp.StartsWith(' ') || temp.StartsWith(',') || temp.StartsWith('.') || temp.StartsWith('!') || temp.StartsWith('?'))) + { + temp = temp.Remove(0, 1); + doRepeat = true; + } + if (temp.Length > 0 && s[0].ToString(CultureInfo.InvariantCulture) != s[0].ToString(CultureInfo.InvariantCulture).ToLower()) + { + temp = char.ToUpper(temp[0]) + temp.Substring(1); + doRepeat = true; + } + + if (pre.EndsWith(' ') && temp.StartsWith('-')) + temp = temp.Remove(0, 1); + + if (pre.EndsWith(',') && temp.StartsWith('—')) + pre = pre.TrimEnd(',') + " "; + temp = pre + temp; + } + + if (temp.EndsWith(Environment.NewLine + "- ")) + temp = temp.Remove(temp.Length - 4, 4); + + var st = new StripableText(temp); + if (st.StrippedText.Length == 0) + return string.Empty; + + if (!temp.Contains(Environment.NewLine) && text.Contains(Environment.NewLine) && temp.StartsWith('-')) + temp = temp.Remove(0, 1).Trim(); + + text = temp; + } + } + } + } + var lines = text.SplitToLines(); + if (text != oldText && lines.Length == 2) + { + if (lines[0] == "-" && lines[1] == "-") + return string.Empty; + if (lines[0].StartsWith('-') && lines[0].Length > 1 && lines[1].Trim() == "-") + return lines[0].Remove(0, 1).Trim(); + if (lines[1].StartsWith('-') && lines[1].Length > 1 && lines[0].Trim() == "-") + return lines[1].Remove(0, 1).Trim(); + if (lines[1].StartsWith("-", StringComparison.Ordinal) && lines[1].Length > 4 && lines[0].Trim() == "-") + return "" + lines[1].Remove(0, 4).Trim(); + if (lines[0].Length > 1 && (lines[1] == "-") || lines[1] == "." || lines[1] == "!" || lines[1] == "?") + { + if (oldText.Contains(Environment.NewLine + "-") && lines[0].StartsWith('-')) + lines[0] = lines[0].Remove(0, 1); + return lines[0].Trim(); + } + if (HtmlUtil.RemoveHtmlTags(lines[0], false).Trim() == "-") + { + if (HtmlUtil.RemoveHtmlTags(lines[1], false).Trim() == "-") + return string.Empty; + if (lines[1].StartsWith('-') && lines[1].Length > 1) + return lines[1].Remove(0, 1).Trim(); + if (lines[1].StartsWith("-", StringComparison.Ordinal) && lines[1].Length > 4) + return "" + lines[1].Remove(0, 4).Trim(); + return lines[1]; + } + if (HtmlUtil.RemoveHtmlTags(lines[1], false).Trim() == "-") + { + if (HtmlUtil.RemoveHtmlTags(lines[0], false).Trim() == "-") + return string.Empty; + if (lines[0].StartsWith('-') && lines[0].Length > 1) + return lines[0].Remove(0, 1).Trim(); + if (lines[0].StartsWith("-", StringComparison.Ordinal) && lines[0].Length > 4) + return "" + lines[0].Remove(0, 4).Trim(); + return lines[0]; + } + } + if (lines.Length == 2 && string.IsNullOrWhiteSpace(lines[1].Replace(".", string.Empty).Replace("?", string.Empty).Replace("!", string.Empty).Replace("-", string.Empty).Replace("—", string.Empty))) + { + text = lines[0]; + lines = text.SplitToLines(); + } + else if (lines.Length == 2 && string.IsNullOrWhiteSpace(lines[0].Replace(".", string.Empty).Replace("?", string.Empty).Replace("!", string.Empty).Replace("-", string.Empty).Replace("—", string.Empty))) + { + text = lines[1]; + lines = text.SplitToLines(); + } + if (text != oldText && lines.Length == 1 && Utilities.GetNumberOfLines(oldText) == 2) + { + if ((oldText.StartsWith('-') || oldText.StartsWith("-", StringComparison.Ordinal)) && + (oldText.Contains("." + Environment.NewLine) || oldText.Contains("." + Environment.NewLine) || + oldText.Contains("!" + Environment.NewLine) || oldText.Contains("!" + Environment.NewLine) || + oldText.Contains("?" + Environment.NewLine) || oldText.Contains("?" + Environment.NewLine))) + { + if (text.StartsWith("-")) + text = "" + text.Remove(0, 4).TrimStart(); + else + text = text.TrimStart('-').TrimStart(); + } + else if ((oldText.Contains(Environment.NewLine + "-") || oldText.Contains(Environment.NewLine + "-")) && + (oldText.Contains("." + Environment.NewLine) || oldText.Contains("." + Environment.NewLine) || + oldText.Contains("!" + Environment.NewLine) || oldText.Contains("!" + Environment.NewLine) || + oldText.Contains("?" + Environment.NewLine) || oldText.Contains("?" + Environment.NewLine))) + { + if (text.StartsWith("-")) + text = "" + text.Remove(0, 4).TrimStart(); + else + text = text.TrimStart('-').TrimStart(); + } + } + return text; + } + + private string RemoveStartEndTags(string text) + { + string newText = text; + string s = text; + if (s.StartsWith('[') && s.IndexOf(']') > 0 && Settings.RemoveTextBetweenSquares) + newText = s.Remove(0, s.IndexOf(']') + 1); + else if (s.StartsWith('{') && s.IndexOf('}') > 0 && Settings.RemoveTextBetweenBrackets) + newText = s.Remove(0, s.IndexOf('}') + 1); + else if (s.StartsWith('?') && s.IndexOf('?', 1) > 0 && Settings.RemoveTextBetweenQuestionMarks) + newText = s.Remove(0, s.IndexOf('?', 1) + 1); + else if (s.StartsWith('(') && s.IndexOf(')') > 0 && Settings.RemoveTextBetweenParentheses) + newText = s.Remove(0, s.IndexOf(')') + 1); + else if (s.StartsWith('[') && s.IndexOf("]:", StringComparison.Ordinal) > 0 && Settings.RemoveTextBetweenSquares) + newText = s.Remove(0, s.IndexOf("]:", StringComparison.Ordinal) + 2); + else if (s.StartsWith('{') && s.IndexOf("}:", StringComparison.Ordinal) > 0 && Settings.RemoveTextBetweenBrackets) + newText = s.Remove(0, s.IndexOf("}:", StringComparison.Ordinal) + 2); + else if (s.StartsWith('?') && s.IndexOf("?:", 1, StringComparison.Ordinal) > 0 && Settings.RemoveTextBetweenQuestionMarks) + newText = s.Remove(0, s.IndexOf("?:", StringComparison.Ordinal) + 2); + else if (s.StartsWith('(') && s.IndexOf("):", StringComparison.Ordinal) > 0 && Settings.RemoveTextBetweenParentheses) + newText = s.Remove(0, s.IndexOf("):", StringComparison.Ordinal) + 2); + else if (Settings.RemoveTextBetweenCustomTags && + s.Length > 0 && Settings.CustomEnd.Length > 0 && Settings.CustomStart.Length > 0 && + s.StartsWith(Settings.CustomStart) && s.LastIndexOf(Settings.CustomEnd, StringComparison.Ordinal) > 0) + newText = s.Remove(0, s.LastIndexOf(Settings.CustomEnd, StringComparison.Ordinal) + Settings.CustomEnd.Length); + if (newText != text) + newText = newText.TrimStart(' '); + return newText; + } + + public string RemoveHearImpairedTags(string text) + { + string preAssTag = string.Empty; + if (text.StartsWith("{\\", StringComparison.Ordinal) && text.IndexOf('}') > 0) + { + int indexOfEndBracket = text.IndexOf('}') + 1; + preAssTag = text.Substring(0, indexOfEndBracket); + text = text.Remove(0, indexOfEndBracket).TrimStart(); + } + if (Settings.RemoveTextBetweenSquares) + { + text = RemoveTextBetweenTags("[", "]:", text); + text = RemoveTextBetweenTags("[", "]", text); + } + if (Settings.RemoveTextBetweenBrackets) + { + text = RemoveTextBetweenTags("{", "}:", text); + text = RemoveTextBetweenTags("{", "}", text); + } + if (Settings.RemoveTextBetweenQuestionMarks) + { + text = RemoveTextBetweenTags("?", "?:", text); + text = RemoveTextBetweenTags("?", "?", text); + } + if (Settings.RemoveTextBetweenParentheses) + { + text = RemoveTextBetweenTags("(", "):", text); + text = RemoveTextBetweenTags("(", ")", text); + } + if (Settings.RemoveTextBetweenCustomTags && Settings.CustomStart.Length > 0 && Settings.CustomEnd.Length > 0) + { + text = RemoveTextBetweenTags(Settings.CustomStart, Settings.CustomEnd, text); + } + if (string.IsNullOrWhiteSpace(text)) + return string.Empty; + return preAssTag + text.TrimStart(); + } + + private bool HasHearImpairedText(string text) + { + return RemoveHearImpairedTags(text) != text; + } + + public bool HasHearImpariedTagsAtStart(string text) + { + if (Settings.OnlyIfInSeparateLine) + return StartAndEndsWithHearImpariedTags(text); + return HasHearImpairedText(text); + } + + public bool HasHearImpariedTagsAtEnd(string text) + { + if (Settings.OnlyIfInSeparateLine) + return StartAndEndsWithHearImpariedTags(text); + return HasHearImpairedText(text); + } + + private bool StartAndEndsWithHearImpariedTags(string text) + { + return (text.StartsWith('[') && text.EndsWith(']') && !text.Trim('[').Contains('[') && Settings.RemoveTextBetweenSquares) || + (text.StartsWith('{') && text.EndsWith('}') && !text.Trim('{').Contains('{') && Settings.RemoveTextBetweenBrackets) || + (text.StartsWith('?') && text.EndsWith('?') && !text.Trim('?').Contains('?') && Settings.RemoveTextBetweenQuestionMarks) || + (text.StartsWith('(') && text.EndsWith(')') && !text.Trim('(').Contains('(') && Settings.RemoveTextBetweenParentheses) || + (text.StartsWith('[') && text.EndsWith("]:", StringComparison.Ordinal) && !text.Trim('[').Contains('[') && Settings.RemoveTextBetweenSquares) || + (text.StartsWith('{') && text.EndsWith("}:", StringComparison.Ordinal) && !text.Trim('{').Contains('{') && Settings.RemoveTextBetweenBrackets) || + (text.StartsWith('?') && text.EndsWith("?:", StringComparison.Ordinal) && !text.Trim('?').Contains('?') && Settings.RemoveTextBetweenQuestionMarks) || + (text.StartsWith('(') && text.EndsWith("):", StringComparison.Ordinal) && !text.Trim('(').Contains('(') && Settings.RemoveTextBetweenParentheses) || + (Settings.RemoveTextBetweenCustomTags && + Settings.CustomStart.Length > 0 && Settings.CustomEnd.Length > 0 && + text.StartsWith(Settings.CustomStart, StringComparison.Ordinal) && text.EndsWith(Settings.CustomEnd, StringComparison.Ordinal)); + } + + private static string RemoveTextBetweenTags(string startTag, string endTag, string text) + { + text = text.Trim(); + if (startTag == "?" || endTag == "?") + { + if (text.StartsWith(startTag) && text.EndsWith(endTag)) + return string.Empty; + return text; + } + + int start = text.IndexOf(startTag, StringComparison.Ordinal); + if (start < 0 || start == text.Length - 1) + return text; + + int end = text.IndexOf(endTag, start + 1, StringComparison.Ordinal); + while (start >= 0 && end > start) + { + text = text.Remove(start, (end - start) + 1); + if (start > 3 && start < text.Length - 1 && + text.Substring(0, start + 1).EndsWith(" :", StringComparison.Ordinal) && + ".!?".Contains(text[start - 2])) + { + text = text.Remove(start - 1, 2); + } + + start = text.IndexOf(startTag, StringComparison.Ordinal); + if (start >= 0 && start < text.Length - 1) + end = text.IndexOf(endTag, start + 1, StringComparison.Ordinal); + else + break; + } + return text.FixExtraSpaces().TrimEnd(); + } + + public string RemoveLineIfAllUppercase(string text) + { + if (!Settings.RemoveIfAllUppercase) + return text; + + var lines = text.SplitToLines(); + var sb = new StringBuilder(); + foreach (var line in lines) + { + var lineNoHtml = HtmlUtil.RemoveHtmlTags(line, true); + var tmp = lineNoHtml.TrimEnd('.', '!', '?', ':').Trim(); + if (lineNoHtml == lineNoHtml.ToUpper()) + { + tmp = tmp.Trim(' ', '-', '—'); + if (tmp.Length == 1 || tmp == "YES" || tmp == "NO" || tmp == "WHY" || tmp == "HI") + { + sb.AppendLine(line); + } + } + else + { + sb.AppendLine(line); + } + } + return sb.ToString().Trim(); + } + + } +} diff --git a/libse/Forms/RemoveTextForHISettings.cs b/libse/Forms/RemoveTextForHISettings.cs new file mode 100644 index 000000000..ad69fdcec --- /dev/null +++ b/libse/Forms/RemoveTextForHISettings.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; + +namespace Nikse.SubtitleEdit.Core.Forms +{ + public class RemoveTextForHISettings + { + public bool OnlyIfInSeparateLine { get; set; } + public bool RemoveIfAllUppercase { get; set; } + public bool RemoveTextBeforeColon { get; set; } + public bool RemoveTextBeforeColonOnlyUppercase { get; set; } + public bool ColonSeparateLine { get; set; } + public bool RemoveWhereContains { get; set; } + public List RemoveIfTextContains { get; set; } + public bool RemoveTextBetweenCustomTags { get; set; } + public bool RemoveInterjections { get; set; } + public bool RemoveTextBetweenSquares { get; set; } + public bool RemoveTextBetweenBrackets { get; set; } + public bool RemoveTextBetweenQuestionMarks { get; set; } + public bool RemoveTextBetweenParentheses { get; set; } + public string CustomStart { get; set; } + public string CustomEnd { get; set; } + + public RemoveTextForHISettings() + { + OnlyIfInSeparateLine = Configuration.Settings.RemoveTextForHearingImpaired.RemoveTextBetweenOnlySeperateLines; + RemoveIfAllUppercase = Configuration.Settings.RemoveTextForHearingImpaired.RemoveIfAllUppercase; + RemoveTextBeforeColon = Configuration.Settings.RemoveTextForHearingImpaired.RemoveTextBeforeColon; + RemoveTextBeforeColonOnlyUppercase = Configuration.Settings.RemoveTextForHearingImpaired.RemoveTextBeforeColonOnlyIfUppercase; + ColonSeparateLine = Configuration.Settings.RemoveTextForHearingImpaired.RemoveTextBeforeColonOnlyOnSeparateLine; + RemoveWhereContains = Configuration.Settings.RemoveTextForHearingImpaired.RemoveIfContains; + RemoveIfTextContains = new List(); + foreach (string item in Configuration.Settings.RemoveTextForHearingImpaired.RemoveIfContainsText.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) + { + RemoveIfTextContains.Add(item.Trim()); + } + RemoveTextBetweenCustomTags = Configuration.Settings.RemoveTextForHearingImpaired.RemoveTextBetweenCustom; + RemoveInterjections = Configuration.Settings.RemoveTextForHearingImpaired.RemoveInterjections; + RemoveTextBetweenSquares = Configuration.Settings.RemoveTextForHearingImpaired.RemoveTextBetweenBrackets; + RemoveTextBetweenBrackets = Configuration.Settings.RemoveTextForHearingImpaired.RemoveTextBetweenCurlyBrackets; + RemoveTextBetweenQuestionMarks = Configuration.Settings.RemoveTextForHearingImpaired.RemoveTextBetweenQuestionMarks; + RemoveTextBetweenParentheses = Configuration.Settings.RemoveTextForHearingImpaired.RemoveTextBetweenParentheses; + CustomStart = Configuration.Settings.RemoveTextForHearingImpaired.RemoveTextBetweenCustomBefore; + CustomEnd = Configuration.Settings.RemoveTextForHearingImpaired.RemoveTextBetweenCustomAfter; + } + + } +} \ No newline at end of file diff --git a/libse/Forms/SplitLongLinesHelper.cs b/libse/Forms/SplitLongLinesHelper.cs new file mode 100644 index 000000000..5695da70b --- /dev/null +++ b/libse/Forms/SplitLongLinesHelper.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; + +namespace Nikse.SubtitleEdit.Core.Forms +{ + public static class SplitLongLinesHelper + { + public static bool QualifiesForSplit(string text, int singleLineMaxCharacters, int totalLineMaxCharacters) + { + string s = HtmlUtil.RemoveHtmlTags(text.Trim(), true); + if (s.Length > totalLineMaxCharacters) + return true; + + var arr = s.SplitToLines(); + foreach (string line in arr) + { + if (line.Length > singleLineMaxCharacters) + return true; + } + + var tempText = Utilities.UnbreakLine(text); + if (Utilities.CountTagInText(tempText, '-') == 2 && (text.StartsWith('-') || text.StartsWith("-"))) + { + var idx = tempText.IndexOfAny(new[] { ". -", "! -", "? -" }, StringComparison.Ordinal); + if (idx > 1) + { + idx++; + string dialogText = tempText.Remove(idx, 1).Insert(idx, Environment.NewLine); + foreach (string line in dialogText.SplitToLines()) + { + if (line.Length > singleLineMaxCharacters) + return true; + } + } + } + return false; + } + + public static Subtitle SplitLongLinesInSubtitle(Subtitle subtitle, int totalLineMaxCharacters, int singleLineMaxCharacters) + { + var splittedIndexes = new List(); + var autoBreakedIndexes = new List(); + var splittedSubtitle = new Subtitle(); + string language = Utilities.AutoDetectGoogleLanguage(subtitle); + for (int i = 0; i < subtitle.Paragraphs.Count; i++) + { + bool added = false; + var p = subtitle.GetParagraphOrDefault(i); + if (p != null && p.Text != null) + { + if (QualifiesForSplit(p.Text, singleLineMaxCharacters, totalLineMaxCharacters)) + { + var text = Utilities.AutoBreakLine(p.Text, language); + if (!QualifiesForSplit(text, singleLineMaxCharacters, totalLineMaxCharacters)) + { + var newParagraph = new Paragraph(p) { Text = text }; + autoBreakedIndexes.Add(splittedSubtitle.Paragraphs.Count); + splittedSubtitle.Paragraphs.Add(newParagraph); + added = true; + } + else + { + if (text.Contains(Environment.NewLine)) + { + var arr = text.SplitToLines(); + if (arr.Length == 2) + { + var minMsBtwnLnBy2 = Configuration.Settings.General.MinimumMillisecondsBetweenLines / 2; + int spacing1 = minMsBtwnLnBy2; + int spacing2 = minMsBtwnLnBy2; + if (Configuration.Settings.General.MinimumMillisecondsBetweenLines % 2 == 1) + spacing2++; + + double duration = p.Duration.TotalMilliseconds / 2.0; + var newParagraph1 = new Paragraph(p); + var newParagraph2 = new Paragraph(p); + newParagraph1.Text = Utilities.AutoBreakLine(arr[0], language); + newParagraph1.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + duration - spacing1; + newParagraph2.Text = Utilities.AutoBreakLine(arr[1], language); + newParagraph2.StartTime.TotalMilliseconds = newParagraph1.EndTime.TotalMilliseconds + spacing2; + + splittedIndexes.Add(splittedSubtitle.Paragraphs.Count); + splittedIndexes.Add(splittedSubtitle.Paragraphs.Count + 1); + + string p1 = HtmlUtil.RemoveHtmlTags(newParagraph1.Text); + var len = p1.Length - 1; + if (p1.Length > 0 && (p1[len] == '.' || p1[len] == '!' || p1[len] == '?' || p1[len] == ':' || p1[len] == ')' || p1[len] == ']' || p1[len] == '♪')) + { + if (newParagraph1.Text.StartsWith('-') && newParagraph2.Text.StartsWith('-')) + { + newParagraph1.Text = newParagraph1.Text.Remove(0, 1).Trim(); + newParagraph2.Text = newParagraph2.Text.Remove(0, 1).Trim(); + } + else if (newParagraph1.Text.StartsWith("-", StringComparison.Ordinal) && newParagraph2.Text.StartsWith('-')) + { + newParagraph1.Text = newParagraph1.Text.Remove(3, 1).Trim(); + if (newParagraph1.Text.StartsWith(" ", StringComparison.Ordinal)) + newParagraph1.Text = newParagraph1.Text.Remove(3, 1).Trim(); + newParagraph2.Text = newParagraph2.Text.Remove(0, 1).Trim(); + } + } + else + { + if (newParagraph1.Text.EndsWith("", StringComparison.Ordinal)) + { + const string post = ""; + newParagraph1.Text = newParagraph1.Text.Remove(newParagraph1.Text.Length - post.Length); + } + //newParagraph1.Text += comboBoxLineContinuationEnd.Text.TrimEnd() + post; + + if (newParagraph2.Text.StartsWith("", StringComparison.Ordinal)) + { + const string pre = ""; + newParagraph2.Text = newParagraph2.Text.Remove(0, pre.Length); + } + //newParagraph2.Text = pre + comboBoxLineContinuationBegin.Text + newParagraph2.Text; + } + + var indexOfItalicOpen1 = newParagraph1.Text.IndexOf("", StringComparison.Ordinal); + if (indexOfItalicOpen1 >= 0 && indexOfItalicOpen1 < 10 && newParagraph1.Text.IndexOf("", StringComparison.Ordinal) < 0 && + newParagraph2.Text.Contains("") && newParagraph2.Text.IndexOf("", StringComparison.Ordinal) < 0) + { + newParagraph1.Text += ""; + newParagraph2.Text = "" + newParagraph2.Text; + } + + splittedSubtitle.Paragraphs.Add(newParagraph1); + splittedSubtitle.Paragraphs.Add(newParagraph2); + added = true; + } + } + } + } + } + if (!added) + splittedSubtitle.Paragraphs.Add(new Paragraph(p)); + } + splittedSubtitle.Renumber(); + return splittedSubtitle; + } + + } +} \ No newline at end of file diff --git a/libse/Fourier.cs b/libse/Fourier.cs new file mode 100644 index 000000000..557e637d9 --- /dev/null +++ b/libse/Fourier.cs @@ -0,0 +1,162 @@ +using System; + +namespace Nikse.SubtitleEdit.Core +{ + /// + /// Fourier transform + /// + /// ***************************************************************************** + /// * + /// * Copyright (c) 2002, Wilhelm Kurz. All Rights Reserved. + /// * wkurz@foni.net + /// * + /// * This file is provided for demonstration and educational uses only. + /// * Permission to use, copy, modify and distribute this file for + /// * any purpose and without fee is hereby granted. + /// * + /// ***************************************************************************** + /// Converted/optimized by Nikse from vb code: http://www.wilhelm-kurz-software.de/dynaplot/applicationnotes/spectrogram.htm + /// + internal class Fourier + { + public const double W0Hanning = 0.5; + public const double W0Hamming = 0.54; + public const double W0Blackman = 0.42; + private const double Pi = 3.14159265358979; + + private double[] cosarray; + private double[] sinarray; + private bool _forward; + private int _arraySize; + private int _ldArraysize = 0; + + public Fourier(int arraySize, bool forward) + { + _arraySize = arraySize; + _forward = forward; + cosarray = new double[arraySize]; + sinarray = new double[arraySize]; + + double sign = 1.0; + if (forward) + sign = -1.0; + + double phase0 = 2.0 * Pi / arraySize; + for (int i = 0; i <= arraySize - 1; i++) + { + sinarray[i] = sign * Math.Sin(phase0 * i); + cosarray[i] = Math.Cos(phase0 * i); + } + + int j = _arraySize; + while (j != 1) + { + _ldArraysize++; + j /= 2; + } + } + + public void MagnitudeSpectrum(double[] real, double[] imag, double w0, double[] magnitude) + { + int i; + magnitude[0] = Math.Sqrt(SquareSum(real[0], imag[0])); + for (i = 1; i <= (_arraySize / 2 - 1); i++) + magnitude[i] = (Math.Sqrt(SquareSum(real[i], imag[i]) + SquareSum(real[_arraySize - i], imag[_arraySize - i]))) / w0; + } + + public static double Hanning(int n, int j) + { + return W0Hanning - 0.5 * Math.Cos(2.0 * Pi * j / n); + } + + public static double Hamming(int n, int j) + { + return W0Hamming - 0.46 * Math.Cos(2.0 * Pi * j / n); + } + + public static double Blackman(int n, int j) + { + return W0Blackman - 0.5 * Math.Cos(2.0 * Pi * j / n) + 0.08 * Math.Cos(4.0 * Pi * j / n); + } + + private static void Swap(ref double a, ref double b) + { + double temp = a; + a = b; + b = temp; + } + + private static double SquareSum(double a, double b) + { + return a * a + b * b; + } + + public void FourierTransform(double[] real, double[] imag) + { + int i; + if (_forward) + { + for (i = 0; i <= _arraySize - 1; i++) + { + real[i] /= _arraySize; + imag[i] /= _arraySize; + } + } + + int k; + int j = 0; + for (i = 0; i <= _arraySize - 2; i++) + { + if (i < j) + { + Swap(ref real[i], ref real[j]); + Swap(ref imag[i], ref imag[j]); + } + k = _arraySize / 2; + while (k <= j) + { + j -= k; + k /= 2; + } + j += k; + } + + int a = 2; + int b = 1; + for (int count = 1; count <= _ldArraysize; count++) + { + int c0 = _arraySize / a; + int c1 = 0; + for (k = 0; k <= b - 1; k++) + { + i = k; + while (i < _arraySize) + { + int arg = i + b; + double prodreal; + double prodimag; + if (k == 0) + { + prodreal = real[arg]; + prodimag = imag[arg]; + } + else + { + prodreal = real[arg] * cosarray[c1] - imag[arg] * sinarray[c1]; + prodimag = real[arg] * sinarray[c1] + imag[arg] * cosarray[c1]; + } + real[arg] = real[i] - prodreal; + imag[arg] = imag[i] - prodimag; + real[i] += prodreal; + imag[i] += prodimag; + i += a; + } + c1 += c0; + } + a *= 2; + b *= 2; + } + } + + } +} diff --git a/libse/HistoryItem.cs b/libse/HistoryItem.cs new file mode 100644 index 000000000..4ad65899c --- /dev/null +++ b/libse/HistoryItem.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; + +namespace Nikse.SubtitleEdit.Core +{ + public class HistoryItem + { + public int Index { get; set; } + public DateTime Timestamp { get; set; } + public string Description { get; set; } + public string FileName { get; set; } + public DateTime FileModified { get; set; } + public Subtitle Subtitle { get; set; } + public string SubtitleFormatFriendlyName { get; set; } + public Subtitle OriginalSubtitle { get; set; } + public string OriginalSubtitleFileName { get; set; } + public List RedoParagraphs { get; set; } + public List RedoParagraphsAlternate { get; set; } + public int RedoLineIndex { get; set; } + public int RedoLinePosition { get; set; } + public int RedoLinePositionAlternate { get; set; } + public string RedoFileName { get; set; } + public DateTime RedoFileModified { get; set; } + public string RedoOriginalFileName { get; set; } + public int LineIndex { get; set; } + public int LinePosition { get; set; } + public int LinePositionAlternate { get; set; } + + public HistoryItem(int index, Subtitle subtitle, string description, string fileName, DateTime fileModified, string subtitleFormatFriendlyName, Subtitle originalSubtitle, string originalSubtitleFileName, int lineIndex, int linePosition, int linePositionAlternate) + { + Index = index; + Timestamp = DateTime.Now; + Subtitle = new Subtitle(subtitle); + Description = description; + FileName = fileName; + FileModified = fileModified; + SubtitleFormatFriendlyName = subtitleFormatFriendlyName; + OriginalSubtitle = new Subtitle(originalSubtitle); + OriginalSubtitleFileName = originalSubtitleFileName; + LineIndex = lineIndex; + LinePosition = linePosition; + LinePositionAlternate = linePositionAlternate; + RedoLineIndex = -1; + RedoLinePosition = -1; + } + + public string ToHHMMSS() + { + return string.Format("{0:00}:{1:00}:{2:00}", Timestamp.Hour, Timestamp.Minute, Timestamp.Second); + } + } +} \ No newline at end of file diff --git a/libse/HtmlUtil.cs b/libse/HtmlUtil.cs new file mode 100644 index 000000000..2542a5bd6 --- /dev/null +++ b/libse/HtmlUtil.cs @@ -0,0 +1,669 @@ +using System; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core +{ + /// + /// HTML specific string manipulations. + /// + public static class HtmlUtil + { + public const string TagItalic = "i"; + public const string TagBold = "b"; + public const string TagUnderline = "u"; + public const string TagParagraph = "p"; + public const string TagFont = "font"; + public const string TagCyrillicI = "\u0456"; // Cyrillic Small Letter Byelorussian-Ukrainian i (http://graphemica.com/%D1%96) + + private static readonly Regex TagOpenRegex = new Regex(@"<\s*(?:/\s*)?(\w+)[^>]*>", RegexOptions.Compiled); + + /// + /// Remove all of the specified opening and closing tags from the source HTML string. + /// + /// The source string to search for specified HTML tags. + /// The HTML tags to remove. + /// A new string without the specified opening and closing tags. + public static string RemoveOpenCloseTags(string source, params string[] tags) + { + // This pattern matches these tag formats: + // + // < tag*> + // + // < /tag*> + // + // < / tag*> + return TagOpenRegex.Replace( + source, + m => tags.Contains(m.Groups[1].Value, StringComparer.OrdinalIgnoreCase) ? string.Empty : m.Value); + } + + /// + /// Converts a string to an HTML-encoded string using named character references. + /// + /// The string to encode. + /// An encoded string. + public static string EncodeNamed(string source) + { + if (source == null) + return string.Empty; + + var encoded = new StringBuilder(source.Length); + foreach (var ch in source) + { + switch (ch) + { + case '<': + encoded.Append("<"); + break; + case '>': + encoded.Append(">"); + break; + case '"': + encoded.Append("""); + break; + case '&': + encoded.Append("&"); + break; + case '\'': + encoded.Append("'"); + break; + case ' ': + encoded.Append(" "); + break; + case '–': + encoded.Append("–"); + break; + case '—': + encoded.Append("—"); + break; + case '¡': + encoded.Append("¡"); + break; + case '¿': + encoded.Append("¿"); + break; + case '“': + encoded.Append("“"); + break; + case '”': + encoded.Append("”"); + break; + case '‘': + encoded.Append("‘"); + break; + case '’': + encoded.Append("’"); + break; + case '«': + encoded.Append("«"); + break; + case '»': + encoded.Append("»"); + break; + case '¢': + encoded.Append("¢"); + break; + case '©': + encoded.Append("©"); + break; + case '÷': + encoded.Append("÷"); + break; + case 'µ': + encoded.Append("µ"); + break; + case '·': + encoded.Append("·"); + break; + case '¶': + encoded.Append("¶"); + break; + case '±': + encoded.Append("±"); + break; + case '€': + encoded.Append("€"); + break; + case '£': + encoded.Append("£"); + break; + case '®': + encoded.Append("®"); + break; + case '§': + encoded.Append("§"); + break; + case '™': + encoded.Append("™"); + break; + case '¥': + encoded.Append("¥"); + break; + case 'á': + encoded.Append("á"); + break; + case 'Á': + encoded.Append("Á"); + break; + case 'à': + encoded.Append("à"); + break; + case 'À': + encoded.Append("À"); + break; + case 'â': + encoded.Append("â"); + break; + case 'Â': + encoded.Append("Â"); + break; + case 'å': + encoded.Append("å"); + break; + case 'Å': + encoded.Append("Å"); + break; + case 'ã': + encoded.Append("ã"); + break; + case 'Ã': + encoded.Append("Ã"); + break; + case 'ä': + encoded.Append("ä"); + break; + case 'Ä': + encoded.Append("Ä"); + break; + case 'æ': + encoded.Append("æ"); + break; + case 'Æ': + encoded.Append("Æ"); + break; + case 'ç': + encoded.Append("ç"); + break; + case 'Ç': + encoded.Append("Ç"); + break; + case 'é': + encoded.Append("é"); + break; + case 'É': + encoded.Append("É"); + break; + case 'è': + encoded.Append("è"); + break; + case 'È': + encoded.Append("È"); + break; + case 'ê': + encoded.Append("ê"); + break; + case 'Ê': + encoded.Append("Ê"); + break; + case 'ë': + encoded.Append("ë"); + break; + case 'Ë': + encoded.Append("Ë"); + break; + case 'í': + encoded.Append("í"); + break; + case 'Í': + encoded.Append("Í"); + break; + case 'ì': + encoded.Append("ì"); + break; + case 'Ì': + encoded.Append("Ì"); + break; + case 'î': + encoded.Append("î"); + break; + case 'Î': + encoded.Append("Î"); + break; + case 'ï': + encoded.Append("ï"); + break; + case 'Ï': + encoded.Append("Ï"); + break; + case 'ñ': + encoded.Append("ñ"); + break; + case 'Ñ': + encoded.Append("Ñ"); + break; + case 'ó': + encoded.Append("ó"); + break; + case 'Ó': + encoded.Append("Ó"); + break; + case 'ò': + encoded.Append("ò"); + break; + case 'Ò': + encoded.Append("Ò"); + break; + case 'ô': + encoded.Append("ô"); + break; + case 'Ô': + encoded.Append("Ô"); + break; + case 'ø': + encoded.Append("ø"); + break; + case 'Ø': + encoded.Append("Ø"); + break; + case 'õ': + encoded.Append("õ"); + break; + case 'Õ': + encoded.Append("Õ"); + break; + case 'ö': + encoded.Append("ö"); + break; + case 'Ö': + encoded.Append("Ö"); + break; + case 'ß': + encoded.Append("ß"); + break; + case 'ú': + encoded.Append("ú"); + break; + case 'Ú': + encoded.Append("Ú"); + break; + case 'ù': + encoded.Append("ù"); + break; + case 'Ù': + encoded.Append("Ù"); + break; + case 'û': + encoded.Append("û"); + break; + case 'Û': + encoded.Append("Û"); + break; + case 'ü': + encoded.Append("ü"); + break; + case 'Ü': + encoded.Append("Ü"); + break; + case 'ÿ': + encoded.Append("ÿ"); + break; + default: + if (ch > 127) + encoded.Append("&#" + (int)ch + ";"); + else + encoded.Append(ch); + break; + } + } + return encoded.ToString(); + } + + /// + /// Converts a string to an HTML-encoded string using numeric character references. + /// + /// The string to encode. + /// An encoded string. + public static string EncodeNumeric(string source) + { + if (source == null) + return string.Empty; + + var encoded = new StringBuilder(source.Length); + foreach (var ch in source) + { + if (ch == ' ') + { + encoded.Append("&#"); + encoded.Append(160); //   + encoded.Append(';'); + } + else if (ch > 127 || ch == '<' || ch == '>' || ch == '"' || ch == '&' || ch == '\'') + { + encoded.Append("&#"); + encoded.Append((int)ch); + encoded.Append(';'); + } + else + { + encoded.Append(ch); + } + } + return encoded.ToString(); + } + + public static string RemoveHtmlTags(string s, bool alsoSsaTags = false) + { + if (s == null || s.Length < 3) + return s; + + if (alsoSsaTags) + s = Utilities.RemoveSsaTags(s); + + if (!s.Contains('<')) + return s; + + if (s.Contains("< ")) + s = FixInvalidItalicTags(s); + + return RemoveOpenCloseTags(s, TagItalic, TagBold, TagUnderline, TagParagraph, TagFont, TagCyrillicI); + } + + public static bool IsUrl(string text) + { + if (string.IsNullOrWhiteSpace(text) || text.Length < 6 || !text.Contains('.') || text.Contains(' ')) + return false; + + var allLower = text.ToLower(); + if (allLower.StartsWith("http://", StringComparison.Ordinal) || allLower.StartsWith("https://", StringComparison.Ordinal) || + allLower.StartsWith("www.", StringComparison.Ordinal) || allLower.EndsWith(".org", StringComparison.Ordinal) || + allLower.EndsWith(".com", StringComparison.Ordinal) || allLower.EndsWith(".net", StringComparison.Ordinal)) + return true; + + if (allLower.Contains(".org/") || allLower.Contains(".com/") || allLower.Contains(".net/")) + return true; + + return false; + } + + public static bool StartsWithUrl(string text) + { + if (string.IsNullOrWhiteSpace(text)) + return false; + + var arr = text.Trim().TrimEnd('.').TrimEnd().Split(); + if (arr.Length == 0) + return false; + + return IsUrl(arr[0]); + } + + public static string FixUpperTags(string text) + { + if (string.IsNullOrEmpty(text)) + return text; + var tags = new string[] { "", "", "", "", "", "", "
" }; + var idx = text.IndexOfAny(tags, StringComparison.Ordinal); + while (idx >= 0) + { + var endIdx = text.IndexOf('>', idx + 2); + if (endIdx < idx) + break; + var tag = text.Substring(idx, endIdx - idx).ToLowerInvariant(); + text = text.Remove(idx, endIdx - idx).Insert(idx, tag); + idx = text.IndexOfAny(tags, StringComparison.Ordinal); + } + return text; + } + + public static string FixInvalidItalicTags(string text) + { + const string beginTag = ""; + const string endTag = ""; + + text = text.Replace("< i >", beginTag); + text = text.Replace("< i>", beginTag); + text = text.Replace("", beginTag); + text = text.Replace("< I>", beginTag); + text = text.Replace("", beginTag); + + text = text.Replace("< / i >", endTag); + text = text.Replace("< /i>", endTag); + text = text.Replace("", endTag); + text = text.Replace("< /i>", endTag); + text = text.Replace("< /i >", endTag); + text = text.Replace("", endTag); + text = text.Replace("", endTag); + text = text.Replace("< / i>", endTag); + text = text.Replace("< /I>", endTag); + text = text.Replace("", endTag); + text = text.Replace("< /I>", endTag); + text = text.Replace("< / I >", endTag); + + text = text.Replace(" ", "_@_"); + text = text.Replace(" _@_", "_@_"); + text = text.Replace(" _@_ ", "_@_"); + text = text.Replace("_@_", " "); + + if (text.Contains(beginTag)) + text = text.Replace("", endTag); + else + text = text.Replace("", string.Empty); + + text = text.Replace(beginTag + beginTag, beginTag); + text = text.Replace(endTag + endTag, endTag); + + int italicBeginTagCount = Utilities.CountTagInText(text, beginTag); + int italicEndTagCount = Utilities.CountTagInText(text, endTag); + int noOfLines = Utilities.GetNumberOfLines(text); + if (italicBeginTagCount + italicEndTagCount > 0) + { + if (italicBeginTagCount == 1 && italicEndTagCount == 1 && text.IndexOf(beginTag, StringComparison.Ordinal) > text.IndexOf(endTag, StringComparison.Ordinal)) + { + text = text.Replace(beginTag, "___________@"); + text = text.Replace(endTag, beginTag); + text = text.Replace("___________@", endTag); + } + + if (italicBeginTagCount == 2 && italicEndTagCount == 0) + { + int firstIndex = text.IndexOf(beginTag, StringComparison.Ordinal); + int lastIndex = text.LastIndexOf(beginTag, StringComparison.Ordinal); + int lastIndexWithNewLine = text.LastIndexOf(Environment.NewLine + beginTag, StringComparison.Ordinal) + Environment.NewLine.Length; + if (noOfLines == 2 && lastIndex == lastIndexWithNewLine && firstIndex < 2) + text = text.Replace(Environment.NewLine, "" + Environment.NewLine) + ""; + else if (text.Length > lastIndex + endTag.Length) + text = text.Substring(0, lastIndex) + endTag + text.Substring(lastIndex - 1 + endTag.Length); + else + text = text.Substring(0, lastIndex) + endTag; + } + + if (italicBeginTagCount == 1 && italicEndTagCount == 2) + { + int firstIndex = text.IndexOf(endTag, StringComparison.Ordinal); + if (text.StartsWith("--", StringComparison.Ordinal)) + text = text.Remove(0, 5); + else if (text.StartsWith("- -", StringComparison.Ordinal)) + text = text.Remove(0, 5); + else if (text.StartsWith("- -", StringComparison.Ordinal)) + text = text.Remove(0, 5); + else if (text.StartsWith("- -", StringComparison.Ordinal)) + text = text.Remove(0, 5); + else if (firstIndex == 0) + text = text.Remove(0, 4); + else + text = text.Substring(0, firstIndex) + text.Substring(firstIndex + endTag.Length); + } + + if (italicBeginTagCount == 2 && italicEndTagCount == 1) + { + var lines = text.SplitToLines(); + if (lines.Length == 2 && lines[0].StartsWith("", StringComparison.Ordinal) && lines[0].EndsWith("", StringComparison.Ordinal) && + lines[1].StartsWith("", StringComparison.Ordinal)) + { + text = text.TrimEnd() + ""; + } + else + { + int lastIndex = text.LastIndexOf(beginTag, StringComparison.Ordinal); + if (text.Length > lastIndex + endTag.Length) + text = text.Substring(0, lastIndex) + text.Substring(lastIndex - 1 + endTag.Length); + else + text = text.Substring(0, lastIndex - 1) + endTag; + } + if (text.StartsWith("", StringComparison.Ordinal) && text.EndsWith("", StringComparison.Ordinal) && text.Contains("" + Environment.NewLine + "")) + { + text = text.Replace("" + Environment.NewLine + "", Environment.NewLine); + } + } + + if (italicBeginTagCount == 1 && italicEndTagCount == 0) + { + int lastIndexWithNewLine = text.LastIndexOf(Environment.NewLine + beginTag, StringComparison.Ordinal) + Environment.NewLine.Length; + int lastIndex = text.LastIndexOf(beginTag, StringComparison.Ordinal); + + if (text.StartsWith(beginTag, StringComparison.Ordinal)) + text += endTag; + else if (noOfLines == 2 && lastIndex == lastIndexWithNewLine) + text += endTag; + else + text = text.Replace(beginTag, string.Empty); + } + + if (italicBeginTagCount == 0 && italicEndTagCount == 1) + { + var cleanText = HtmlUtil.RemoveOpenCloseTags(text, HtmlUtil.TagItalic, HtmlUtil.TagBold, HtmlUtil.TagUnderline, HtmlUtil.TagCyrillicI); + bool isFixed = false; + + // Foo. + if (text.EndsWith(endTag, StringComparison.Ordinal) && !cleanText.StartsWith('-') && !cleanText.Contains(Environment.NewLine + "-")) + { + text = beginTag + text; + isFixed = true; + } + + // - Foo | - Foo. + // - Bar. | - Foo. + if (!isFixed && Utilities.GetNumberOfLines(cleanText) == 2) + { + int newLineIndex = text.IndexOf(Environment.NewLine, StringComparison.Ordinal); + if (newLineIndex > 0) + { + var firstLine = text.Substring(0, newLineIndex).Trim(); + var secondLine = text.Substring(newLineIndex + 2).Trim(); + if (firstLine.EndsWith(endTag, StringComparison.Ordinal)) + { + firstLine = beginTag + firstLine; + isFixed = true; + } + if (secondLine.EndsWith(endTag, StringComparison.Ordinal)) + { + secondLine = beginTag + secondLine; + isFixed = true; + } + text = firstLine + Environment.NewLine + secondLine; + } + } + if (!isFixed) + text = text.Replace(endTag, string.Empty); + } + + // - foo. + // - bar. + if (italicBeginTagCount == 0 && italicEndTagCount == 2 && text.Contains(endTag + Environment.NewLine, StringComparison.Ordinal) && text.EndsWith(endTag, StringComparison.Ordinal)) + { + text = text.Replace(endTag, string.Empty); + text = beginTag + text + endTag; + } + + if (italicBeginTagCount == 0 && italicEndTagCount == 2 && text.StartsWith("", StringComparison.Ordinal) && text.EndsWith("", StringComparison.Ordinal)) + { + int firstIndex = text.IndexOf(endTag, StringComparison.Ordinal); + text = text.Remove(firstIndex, endTag.Length).Insert(firstIndex, ""); + } + + // Foo + // Bar + if (italicBeginTagCount == 2 && italicEndTagCount == 2 && Utilities.GetNumberOfLines(text) == 2) + { + int index = text.IndexOf(Environment.NewLine, StringComparison.Ordinal); + if (index > 0 && text.Length > index + (beginTag.Length + endTag.Length)) + { + var firstLine = text.Substring(0, index).Trim(); + var secondLine = text.Substring(index + 2).Trim(); + + if (firstLine.Length > 10 && firstLine.StartsWith("- ", StringComparison.Ordinal) && firstLine.EndsWith(endTag, StringComparison.Ordinal)) + { + text = "- " + firstLine.Remove(0, 5) + Environment.NewLine + secondLine; + text = text.Replace("- ", "- "); + index = text.IndexOf(Environment.NewLine, StringComparison.Ordinal); + firstLine = text.Substring(0, index).Trim(); + secondLine = text.Substring(index + 2).Trim(); + } + if (secondLine.Length > 10 && secondLine.StartsWith("- ", StringComparison.Ordinal) && secondLine.EndsWith(endTag, StringComparison.Ordinal)) + { + text = firstLine + Environment.NewLine + "- " + secondLine.Remove(0, 5); + text = text.Replace("- ", "- "); + index = text.IndexOf(Environment.NewLine, StringComparison.Ordinal); + firstLine = text.Substring(0, index).Trim(); + secondLine = text.Substring(index + 2).Trim(); + } + + if (Utilities.StartsAndEndsWithTag(firstLine, beginTag, endTag) && Utilities.StartsAndEndsWithTag(secondLine, beginTag, endTag)) + { + text = text.Replace(beginTag, String.Empty).Replace(endTag, String.Empty).Trim(); + text = beginTag + text + endTag; + } + } + + //FALCONE: I didn't think
it was going to be you, + var colIdx = text.IndexOf(':'); + if (colIdx > -1 && Utilities.CountTagInText(text, "") + Utilities.CountTagInText(text, "") == 4 && text.Length > colIdx + 1 && !char.IsDigit(text[colIdx + 1])) + { + var firstLine = text.Substring(0, index); + var secondLine = text.Substring(index).TrimStart(); + + var secIdxCol = secondLine.IndexOf(':'); + if (secIdxCol < 0 || !Utilities.IsBetweenNumbers(secondLine, secIdxCol)) + { + var idx = firstLine.IndexOf(':'); + if (idx > 1) + { + var pre = text.Substring(0, idx + 1).TrimStart(); + text = text.Remove(0, idx + 1); + text = FixInvalidItalicTags(text).Trim(); + if (text.StartsWith(" ", StringComparison.OrdinalIgnoreCase)) + text = Utilities.RemoveSpaceBeforeAfterTag(text, ""); + text = pre + " " + text; + } + } + } + } + + //- You think they're they gone? + //- That can't be. + if ((italicBeginTagCount == 3 && italicEndTagCount == 1) && Utilities.GetNumberOfLines(text) == 2) + { + var newLineIdx = text.IndexOf(Environment.NewLine, StringComparison.Ordinal); + var firstLine = text.Substring(0, newLineIdx).Trim(); + var secondLine = text.Substring(newLineIdx).Trim(); + + if ((Utilities.StartsAndEndsWithTag(firstLine, beginTag, beginTag) && Utilities.StartsAndEndsWithTag(secondLine, beginTag, endTag)) || + (Utilities.StartsAndEndsWithTag(secondLine, beginTag, beginTag) && Utilities.StartsAndEndsWithTag(firstLine, beginTag, endTag))) + { + text = text.Replace("", string.Empty); + text = text.Replace("", string.Empty); + text = text.Replace(" ", " ").Trim(); + text = "" + text + ""; + } + } + text = text.Replace("", string.Empty); + text = text.Replace(" ", string.Empty); + text = text.Replace(" ", string.Empty); + } + return text; + } + + } +} \ No newline at end of file diff --git a/libse/IfoParser.cs b/libse/IfoParser.cs new file mode 100644 index 000000000..d661441b0 --- /dev/null +++ b/libse/IfoParser.cs @@ -0,0 +1,619 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Text; +using Nikse.Core; + +namespace Nikse.SubtitleEdit.Core +{ + public class IfoParser : IDisposable + { + public struct AudioStream + { + public int LanguageTypeSpecified; + public string Language; + public string LanguageCode; + public string CodingMode; + public int Channels; + public string Extension; + }; + + public struct VideoStream + { + public string Aspect; + public string Standard; + public string CodingMode; + public string Resolution; + } + + public class VtsVobs + { + public int NumberOfAudioStreams; + public int NumberOfSubtitles; + public VideoStream VideoStream; + public List AudioStreams; + public List Subtitles; + public List SubtitleIDs; + public List SubtitleTypes; + + public List GetAllLanguages() + { + var list = new List(); + for (int i = 0; i < Subtitles.Count; i++) + { + if (i < SubtitleIDs.Count && i < SubtitleTypes.Count) + { + var ids = SubtitleIDs[i].Split(','); + var types = SubtitleTypes[i].Split(','); + if (ids.Length == 2 && ids[0].Trim() == ids[1].Trim() || ids.Length == 3 && ids[0].Trim() == ids[1].Trim() && ids[1].Trim() == ids[2].Trim()) + { + list.Add(Subtitles[i] + " (" + ids[0].Trim() + ")"); + } + else + { + if (ids.Length >= 1 && types.Length >= 1) + { + list.Add(Subtitles[i] + ", " + types[0].Trim() + " (" + ids[0].Trim() + ")"); + } + if (ids.Length >= 2 && types.Length >= 2) + { + list.Add(Subtitles[i] + ", " + types[1].Trim() + " (" + ids[1].Trim() + ")"); + } + if (ids.Length >= 3 && types.Length >= 3) + { + list.Add(Subtitles[i] + ", " + types[2].Trim() + " (" + ids[2].Trim() + ")"); + } + if (ids.Length >= 4 && types.Length >= 4) + { + list.Add(Subtitles[i] + ", " + types[3].Trim() + " (" + ids[3].Trim() + ")"); + } + } + } + } + return list; + } + + public VtsVobs() + { + VideoStream = new VideoStream(); + AudioStreams = new List(); + Subtitles = new List(); + SubtitleIDs = new List(); + SubtitleTypes = new List(); + } + }; + + public class ProgramChain + { + public int NumberOfPgc; + public int NumberOfCells; + public string PlaybackTime; + public List PgcEntryCells; + public List PgcPlaybackTimes; + public List PgcStartTimes; + public List AudioStreamsAvailable; + public List SubtitlesAvailable; + public List ColorLookupTable; + + public ProgramChain() + { + PgcEntryCells = new List(); + PgcPlaybackTimes = new List(); + PgcStartTimes = new List(); + AudioStreamsAvailable = new List(); + SubtitlesAvailable = new List(); + ColorLookupTable = new List(); + } + + public bool Has43Subs { get; set; } + public bool HasWideSubs { get; set; } + public bool HasLetterSubs { get; set; } + public bool HasPanSubs { get; set; } + public bool HasNoSpecificSubs { get; set; } + }; + + public class VtsPgci + { + public int NumberOfProgramChains; + public List ProgramChains; + + public VtsPgci() + { + ProgramChains = new List(); + } + }; + + private readonly List _arrayOfAudioMode = new List { "AC3", "...", "MPEG1", "MPEG2", "LPCM", "...", "DTS" }; + private readonly List _arrayOfAudioExtension = new List { "unspecified", "normal", "for visually impaired", "director's comments", "alternate director's comments" }; + private readonly List _arrayOfAspect = new List { "4:3", "...", "...", "16:9" }; + private readonly List _arrayOfStandard = new List { "NTSC", "PAL", "...", "..." }; + private readonly List _arrayOfCodingMode = new List { "MPEG1", "MPEG2" }; + private readonly List _arrayOfNtscResolution = new List { "720x480", "704x480", "352x480", "352x240" }; + private readonly List _arrayOfPalResolution = new List { "720x576", "704x576", "352x576", "352x288" }; + public static List ArrayOfLanguageCode = new List { " ", "aa", "ab", "af", "am", "ar", "as", "ay", "az", "ba", "be", "bg", "bh", "bi", "bn", "bo", "br", "ca", "co", "cs", "cy", "da", "de", "dz", "el", + "en", "eo", "es", "et", "eu", "fa", "fi", "fj", "fo", "fr", "fy", "ga", "gd", "gl", "gn", "gu", "ha", "he", "hi", "hr", "hu", "hy", "ia", "id", "ie", "ik", + "in", "is", "it", "iu", "iw", "ja", "ji", "jw", "ka", "kk", "kl", "km", "kn", "ko", "ks", "ku", "ky", "la", "ln", "lo", "lt", "lv", "mg", "mi", "mk", "ml", + "mn", "mo", "mr", "ms", "mt", "my", "na", "ne", "nl", "no", "oc", "om", "or", "pa", "pl", "ps", "pt", "qu", "rm", "rn", "ro", "ru", "rw", "sa", "sd", "sg", + "sh", "si", "sk", "sl", "sm", "sn", "so", "sq", "sr", "ss", "st", "su", "sv", "sw", "ta", "te", "tg", "th", "ti", "tk", "tl", "tn", "to", "tr", "ts", "tt", + "tw", "ug", "uk", "ur", "uz", "vi", "vo", "wo", "xh", "yi", "yo", "za", "zh", "zu", ""}; + public static List ArrayOfLanguage = new List { "Not Specified", "Afar", "Abkhazian", "Afrikaans", "Amharic", "Arabic", "Assamese", "Aymara", "Azerbaijani", "Bashkir", "Byelorussian", "Bulgarian", "Bihari", "Bislama", "Bengali; Bangla", "Tibetan", "Breton", "Catalan", "Corsican", "Czech(Ceske)", "Welsh", "Dansk", "Deutsch", "Bhutani", "Greek", + "English", "Esperanto", "Espanol", "Estonian", "Basque", "Persian", "Suomi", "Fiji", "Faroese", "Français", "Frisian", "Irish", "Scots Gaelic", "Galician", "Guarani", "Gujarati", "Hausa", "Hebrew", "Hindi", "Hrvatski", "Magyar", "Armenian", "Interlingua", "Indonesian", "Interlingue", "Inupiak", + "Indonesian", "Islenska", "Italiano", "Inuktitut", "Hebrew", "Japanese", "Yiddish", "Javanese", "Georgian", "Kazakh", "Greenlandic", "Cambodian", "Kannada", "Korean", "Kashmiri", "Kurdish", "Kirghiz", "Latin", "Lingala", "Laothian", "Lithuanian", "Latvian, Lettish", "Malagasy", "Maori", "Macedonian", "Malayalam", + "Mongolian", "Moldavian", "Marathi", "Malay", "Maltese", "Burmese", "Nauru", "Nepali", "Nederlands", "Norsk", "Occitan", "(Afan) Oromo", "Oriya", "Punjabi", "Polish", "Pashto, Pushto", "Portugues", "Quechua", "Rhaeto-Romance", "Kirundi", "Romanian", "Russian", "Kinyarwanda", "Sanskrit", "Sindhi", "Sangho", + "Serbo-Croatian", "Sinhalese", "Slovak", "Slovenian", "Samoan", "Shona", "Somali", "Albanian", "Serbian", "Siswati", "Sesotho", "Sundanese", "Svenska", "Swahili", "Tamil", "Telugu", "Tajik", "Thai", "Tigrinya", "Turkmen", "Tagalog", "Setswana", "Tonga", "Turkish", "Tsonga", "Tatar", + "Twi", "Uighur", "Ukrainian", "Urdu", "Uzbek", "Vietnamese", "Volapuk", "Wolof", "Xhosa", "Yiddish", "Yoruba", "Zhuang", "Chinese", "Zulu", "???"}; + + public VtsPgci VideoTitleSetProgramChainTable { get { return _vtsPgci; } } + public VtsVobs VideoTitleSetVobs { get { return _vtsVobs; } } + public string ErrorMessage { get; private set; } + + private readonly VtsVobs _vtsVobs = new VtsVobs(); + private readonly VtsPgci _vtsPgci = new VtsPgci(); + private FileStream _fs; + + public IfoParser(string fileName) + { + try + { + _fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + + var buffer = new byte[12]; + _fs.Position = 0; + _fs.Read(buffer, 0, 12); + string id = Encoding.UTF8.GetString(buffer); + if (id != "DVDVIDEO-VTS") + { + ErrorMessage = string.Format(Configuration.Settings.Language.DvdSubRip.WrongIfoType, id, Environment.NewLine, fileName); + return; + } + ParseVtsVobs(); + ParseVtsPgci(); + _fs.Close(); + } + catch (Exception exception) + { + ErrorMessage = exception.Message + Environment.NewLine + exception.StackTrace; + } + } + + private void ParseVtsVobs() + { + var buffer = new byte[16]; + + //retrieve video info + _fs.Position = 0x200; + var data = IntToBin(GetEndian(2), 16); + _vtsVobs.VideoStream.CodingMode = _arrayOfCodingMode[BinToInt(MidStr(data, 0, 2))]; + _vtsVobs.VideoStream.Standard = _arrayOfStandard[BinToInt(MidStr(data, 2, 2))]; + _vtsVobs.VideoStream.Aspect = _arrayOfAspect[BinToInt(MidStr(data, 4, 2))]; + if (_vtsVobs.VideoStream.Standard == "PAL") + _vtsVobs.VideoStream.Resolution = _arrayOfPalResolution[BinToInt(MidStr(data, 13, 2))]; + else if (_vtsVobs.VideoStream.Standard == "NTSC") + _vtsVobs.VideoStream.Resolution = _arrayOfNtscResolution[BinToInt(MidStr(data, 13, 2))]; + + //retrieve audio info + _fs.Position = 0x202; //useless but here for readability + _vtsVobs.NumberOfAudioStreams = GetEndian(2); + for (int i = 0; i < _vtsVobs.NumberOfAudioStreams; i++) + { + var audioStream = new AudioStream(); + data = IntToBin(GetEndian(2), 16); + audioStream.LanguageTypeSpecified = Convert.ToInt32(MidStr(data, 4, 2)); + audioStream.CodingMode = _arrayOfAudioMode[(BinToInt(MidStr(data, 0, 3)))]; + audioStream.Channels = BinToInt(MidStr(data, 13, 3)) + 1; + _fs.Read(buffer, 0, 2); + audioStream.LanguageCode = new string(new[] { Convert.ToChar(buffer[0]), Convert.ToChar(buffer[1]) }); + if (ArrayOfLanguageCode.Contains(audioStream.LanguageCode)) + audioStream.Language = ArrayOfLanguage[ArrayOfLanguageCode.IndexOf(audioStream.LanguageCode)]; + _fs.Seek(1, SeekOrigin.Current); + audioStream.Extension = _arrayOfAudioExtension[_fs.ReadByte()]; + _fs.Seek(2, SeekOrigin.Current); + _vtsVobs.AudioStreams.Add(audioStream); + } + + //retrieve subs info (only name) + _fs.Position = 0x254; + _vtsVobs.NumberOfSubtitles = GetEndian(2); + _fs.Position += 2; + for (int i = 0; i < _vtsVobs.NumberOfSubtitles; i++) + { + _fs.Read(buffer, 0, 2); + var languageTwoLetter = new string(new[] { Convert.ToChar(buffer[0]), Convert.ToChar(buffer[1]) }); + _vtsVobs.Subtitles.Add(InterpretLanguageCode(languageTwoLetter)); + _fs.Read(buffer, 0, 2); // reserved for language code extension + code extension + + //switch (buffer[0]) // 4, 8, 10-12 unused + //{ + // // http://dvd.sourceforge.net/dvdinfo/sprm.html + // case 1: subtitleFormat = "(caption/normal size char)"; break; //0 = unspecified caption + // case 2: subtitleFormat = "(caption/large size char)"; break; + // case 3: subtitleFormat = "(caption for children)"; break; + // case 5: subtitleFormat = "(closed caption/normal size char)"; break; + // case 6: subtitleFormat = "(closed caption/large size char)"; break; + // case 7: subtitleFormat = "(closed caption for children)"; break; + // case 9: subtitleFormat = "(forced caption)"; break; + // case 13: subtitleFormat = "(director comments/normal size char)"; break; + // case 14: subtitleFormat = "(director comments/large size char)"; break; + // case 15: subtitleFormat = "(director comments for children)"; break; + //} + + _fs.Position += 2; + } + } + + private static int BinToInt(string p) + { + return Convert.ToInt32(p, 2); + } + + private static string MidStr(string data, int start, int count) + { + return data.Substring(start, count); + } + + private static string IntToBin(int value, int digits) + { + string result = Convert.ToString(value, 2); + while (result.Length < digits) + result = "0" + result; + return result; + } + + private int GetEndian(int count) + { + int result = 0; + for (int i = count; i > 0; i--) + { + int b = _fs.ReadByte(); + result = (result << 8) + b; + } + return result; + } + + private static string InterpretLanguageCode(string code) + { + int i = 0; + while (ArrayOfLanguageCode[i] != code && i < 143) + { + i++; + } + return ArrayOfLanguage[i]; + } + + private void ParseVtsPgci() + { + const int sectorSize = 2048; + + _fs.Position = 0xCC; //Get VTS_PGCI adress + int tableStart = sectorSize * GetEndian(4); + + _fs.Position = tableStart; + _vtsPgci.NumberOfProgramChains = GetEndian(2); + _vtsPgci.ProgramChains = new List(); + + for (int i = 0; i < _vtsPgci.NumberOfProgramChains; i++) + { + //Parse PGC Header + var programChain = new ProgramChain(); + _fs.Position = tableStart + 4 + 8 * (i + 1); //Get PGC adress + int programChainAdress = GetEndian(4); + _fs.Position = tableStart + programChainAdress + 2; //Move to PGC + programChain.NumberOfPgc = _fs.ReadByte(); + programChain.NumberOfCells = _fs.ReadByte(); + programChain.PlaybackTime = InterpretTime(GetEndian(4)); + _fs.Seek(4, SeekOrigin.Current); + + // check if audio streams are available for this PGC + _fs.Position = tableStart + programChainAdress + 0xC; + for (int j = 0; j < _vtsVobs.NumberOfAudioStreams; j++) + { + string temp = IntToBin(_fs.ReadByte(), 8); + programChain.AudioStreamsAvailable.Add(temp[0]); + _fs.Seek(1, SeekOrigin.Current); + } + + // check if subtitles are available for this PGC + _fs.Position = tableStart + programChainAdress + 0x1C; + for (int j = 0; j < _vtsVobs.NumberOfSubtitles; j++) + { + // read and save full subpicture stream info inside program chain + var subtitle = new byte[4]; + _fs.Read(subtitle, 0, 4); + programChain.SubtitlesAvailable.Add(subtitle); + } + + CalculateSubtitleTypes(programChain); + + //Parse Color LookUp Table (CLUT) - offset 00A4, 16*4 (0, Y, Cr, Cb) + _fs.Position = tableStart + programChainAdress + 0xA4; + for (int colorNumber = 0; colorNumber < 16; colorNumber++) + { + var colors = new byte[4]; + _fs.Read(colors, 0, 4); + int y = colors[1] - 16; + int cr = colors[2] - 128; + int cb = colors[3] - 128; + int r = (int)Math.Min(Math.Max(Math.Round(1.1644F * y + 1.596F * cr), 0), 255); + int g = (int)Math.Min(Math.Max(Math.Round(1.1644F * y - 0.813F * cr - 0.391F * cb), 0), 255); + int b = (int)Math.Min(Math.Max(Math.Round(1.1644F * y + 2.018F * cb), 0), 255); + + programChain.ColorLookupTable.Add(Color.FromArgb(r, g, b)); + } + + //Parse Program Map + _fs.Position = tableStart + programChainAdress + 0xE6; + _fs.Position = tableStart + programChainAdress + GetEndian(2); + for (int j = 0; j < programChain.NumberOfPgc; j++) + { + programChain.PgcEntryCells.Add((byte)_fs.ReadByte()); + } + + // Cell Playback Info Table to retrieve duration + _fs.Position = tableStart + programChainAdress + 0xE8; + _fs.Position = tableStart + programChainAdress + GetEndian(2); + var timeArray = new List(); + for (int k = 0; k < programChain.NumberOfPgc; k++) + { + int time = 0; + int max; + if (k == programChain.NumberOfPgc - 1) + max = programChain.NumberOfCells; + else + max = programChain.PgcEntryCells[k + 1] - 1; + for (int j = programChain.PgcEntryCells[k]; j <= max; j++) + { + _fs.Seek(4, SeekOrigin.Current); + time += TimeToMs(GetEndian(4)); + _fs.Seek(16, SeekOrigin.Current); + } + programChain.PgcPlaybackTimes.Add(MsToTime(time)); + timeArray.Add(time); + + //convert to start time + time = 0; + for (int l = 1; l <= k; l++) + { + time += timeArray[l - 1]; + } + if (k == 0) + programChain.PgcStartTimes.Add(MsToTime(0)); + if (k > 0) + programChain.PgcStartTimes.Add(MsToTime(time)); + } + _vtsPgci.ProgramChains.Add(programChain); + } + } + + private void CalculateSubtitleTypes(ProgramChain programChain) + { + // Additional Code to analyse stream bytes + if (_vtsVobs.NumberOfSubtitles > 0) + { + // load the 'last' subpicture stream info, + // because if we have more than one subtitle stream, + // all subtitle positions > 0 + // lastSubtitle[0] is related to 4:3 + // lastSubtitle[1] is related to Wide + // lastSubtitle[2] is related to letterboxed + // lastSubtitle[3] is related to pan&scan + byte[] lastSubtitle = programChain.SubtitlesAvailable[programChain.SubtitlesAvailable.Count - 1]; + + int countSubs = 0; + + // set defaults for all possible subpicture types and positions + programChain.Has43Subs = false; + programChain.HasWideSubs = false; + programChain.HasLetterSubs = false; + programChain.HasPanSubs = false; + programChain.HasNoSpecificSubs = true; + + int pos43Subs = -1; + int posWideSubs = -1; + int posLetterSubs = -1; + int posPanSubs = -1; + + // parse different subtitle bytes + if (lastSubtitle[0] > 0x80) + { + programChain.Has43Subs = true; + countSubs++; // 4:3 + } + if (lastSubtitle[1] > 0) + { + programChain.HasWideSubs = true; + countSubs++; // wide + } + if (lastSubtitle[2] > 0) + { + programChain.HasLetterSubs = true; + countSubs++; // letterboxed + } + if (lastSubtitle[3] > 0) + { + programChain.HasPanSubs = true; + countSubs++; // pan&scan + } + + if (countSubs == 0) + { + // may be, only a 4:3 stream exists + // -> lastSubtitle[0] = 0x80 + } + else + { + if (_vtsVobs.NumberOfSubtitles == 1) + { + // only 1 stream exists, may be letterboxed + // if so we cound't find wide id, because lastSubtitle[1] = 0 !! + // corresponding wide stream byte is 0 => wide id = 0x20 + // letterboxed = 0x21 + if (programChain.HasLetterSubs && !programChain.HasWideSubs) + { + // repair it + programChain.HasWideSubs = true; + } + } + programChain.HasNoSpecificSubs = false; + } + + // subpucture streams start with 0x20 + int subStream = 0x20; + + // Now we know all about available subpicture streams, including position type + // And we can create whole complete definitions for all avalable streams + foreach (byte[] subtitle in programChain.SubtitlesAvailable) + { + if (programChain.HasNoSpecificSubs) + { + // only one unspezified subpicture stream exists + _vtsVobs.SubtitleIDs.Add(string.Format("0x{0:x2}", subStream++)); + _vtsVobs.SubtitleTypes.Add("unspecific"); + } + else + { + // read stream position for evey subtitle type from subtitle byte + if (programChain.Has43Subs) + { + pos43Subs = subtitle[0] - 0x80; + } + if (programChain.HasWideSubs) + { + posWideSubs = subtitle[1]; + } + if (programChain.HasLetterSubs) + { + posLetterSubs = subtitle[2]; + } + if (programChain.HasPanSubs) + { + posPanSubs = subtitle[3]; + } + + // Now we can create subpicture id's and types for every stream + // All used subpicture id's and types will beappended to string, separated by colon + // So it's possible to split it later + string sub = string.Empty; + string subType = string.Empty; + if (programChain.Has43Subs) + { + sub = string.Format("0x{0:x2}", subStream + pos43Subs); + subType = "4:3"; + } + if (programChain.HasWideSubs) + { + if (sub.Length > 0) + { + sub += ", "; + subType += ", "; + } + sub += string.Format("0x{0:x2}", subStream + posWideSubs); + subType += "wide"; + } + if (programChain.HasLetterSubs) + { + if (sub.Length > 0) + { + sub += ", "; + subType += ", "; + } + sub += string.Format("0x{0:x2}", subStream + posLetterSubs); + subType += "letterboxed"; + } + if (programChain.HasPanSubs) + { + if (sub.Length > 0) + { + sub += ", "; + subType += ", "; + } + sub += string.Format("0x{0:x2}", subStream + posPanSubs); + subType += "pan&scan"; + } + + _vtsVobs.SubtitleIDs.Add(sub); + _vtsVobs.SubtitleTypes.Add(subType); + } + } + } + } + + private static int TimeToMs(int time) + { + double fps; + + var temp = IntToBin(time, 32); + var result = StrToInt(IntToHex(BinToInt(MidStr(temp, 0, 8)), 1)) * 3600000; + result = result + StrToInt(IntToHex(BinToInt(MidStr(temp, 8, 8)), 2)) * 60000; + result = result + StrToInt(IntToHex(BinToInt(MidStr(temp, 16, 8)), 2)) * 1000; + if (temp.Substring(24, 2) == "11") + fps = 30; + else + fps = 25; + result += (int)Math.Round((TimeCode.BaseUnit / fps) * StrToFloat(IntToHex(BinToInt(MidStr(temp, 26, 6)), 3))); + return result; + } + + private static double StrToFloat(string p) + { + return Convert.ToDouble(p, System.Globalization.CultureInfo.InvariantCulture); + } + + private static int StrToInt(string p) + { + return int.Parse(p); + } + + private static string IntToHex(int value, int digits) + { + string hex = value.ToString("X"); + + return hex.PadLeft(digits, '0'); + } + + private static string MsToTime(double milliseconds) + { + var ts = TimeSpan.FromMilliseconds(milliseconds); + string s = string.Format("{0:#0}:{1:00}:{2:00}.{3:000}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds); + return s; + } + + private static string InterpretTime(int timeNumber) + { + string timeBytes = IntToBin(timeNumber, 32); + int h = StrToInt(IntToHex(BinToInt(timeBytes.Substring(0, 8)), 1)); + int m = StrToInt(IntToHex(BinToInt(timeBytes.Substring(8, 8)), 2)); + int s = StrToInt(IntToHex(BinToInt(timeBytes.Substring(16, 8)), 2)); + int fps = 25; + if (timeBytes.Substring(24, 2) == "11") + fps = 30; + int milliseconds = (int)Math.Round((TimeCode.BaseUnit / fps) * StrToFloat(IntToHex(BinToInt(timeBytes.Substring(26, 6)), 3))); + var ts = new TimeSpan(0, h, m, s, milliseconds); + return MsToTime(ts.TotalMilliseconds); + } + + private void ReleaseManagedResources() + { + if (_fs != null) + { + _fs.Dispose(); + _fs = null; + } + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + ReleaseManagedResources(); + } + } + + } +} \ No newline at end of file diff --git a/libse/ImageSplitter.cs b/libse/ImageSplitter.cs new file mode 100644 index 000000000..4c910cca0 --- /dev/null +++ b/libse/ImageSplitter.cs @@ -0,0 +1,691 @@ +//using System; +//using System.Collections.Generic; +//using System.Drawing; + +//namespace Nikse.SubtitleEdit.Logic +//{ +// public class ImageSplitter +// { +// public static bool IsColorClose(Color a, Color b, int tolerance) +// { +// if (a.A < 120 && b.A < 120) +// return true; // transparent + +// if (a.A > 250 && a.R > 90 && a.G > 90 && a.B > 90 && +// b.A > 250 && b.R > 90 && b.G > 90 && b.B > 90) +// return true; // dark, non transparent + +// int diff = (a.R + a.G + a.B) - (b.R + b.G + b.B); +// return diff < tolerance && diff > -tolerance; +// } + +// public static Bitmap Copy(Bitmap sourceBitmap, Rectangle section) +// { +// // Create the new bitmap and associated graphics object +// var bmp = new Bitmap(section.Width, section.Height); +// Graphics g = Graphics.FromImage(bmp); + +// // Draw the specified section of the source bitmap to the new one +// g.DrawImage(sourceBitmap, 0, 0, section, GraphicsUnit.Pixel); + +// // Clean up +// g.Dispose(); + +// // Return the bitmap +// return bmp; +// } + +// public static Bitmap CropTopAndBottom(Bitmap bmp, out int topCropping) +// { +// int startTop = 0; +// int maxTop = bmp.Height-2; +// if (maxTop > bmp.Height) +// maxTop = bmp.Height; +// for (int y = 0; y < maxTop; y++) +// { +// bool allTransparent = true; +// for (int x = 1; x < bmp.Width - 1; x++) +// { +// Color c = bmp.GetPixel(x, y); +// if (c.A != 0) +// { +// allTransparent = false; +// break; +// } +// } +// if (!allTransparent) +// break; +// startTop++; +// } +// if (startTop > 9) +// startTop -= 5; // if top space > 9, then allways leave blank 5 pixels on top (so . is not confused with '). +// topCropping = startTop; + +// for (int y = bmp.Height-1; y > 3; y--) +// { +// bool allTransparent = true; +// for (int x = 1; x < bmp.Width-1; x++) +// { +// Color c = bmp.GetPixel(x, y); +// if (c.A != 0) +// { +// allTransparent = false; +// break; +// } +// } +// if (allTransparent == false) +// return Copy(bmp, new Rectangle(0, startTop, bmp.Width - 1, y-startTop+1)); +// } +// return bmp; +// } + +// public static Bitmap CropTopAndBottom(Bitmap bmp, out int topCropping, int maxDifferentPixelsOnLine) +// { +// int startTop = 0; +// int maxTop = bmp.Height - 2; +// if (maxTop > bmp.Height) +// maxTop = bmp.Height; + +// for (int y = 0; y < maxTop; y++) +// { +// int difference = 0; +// bool allTransparent = true; +// for (int x = 1; x < bmp.Width - 1; x++) +// { +// Color c = bmp.GetPixel(x, y); +// if (c.A != 0) +// { +// difference++; +// if (difference >= maxDifferentPixelsOnLine) +// { +// allTransparent = false; +// break; +// } +// } +// } +// if (!allTransparent) +// break; +// startTop++; +// } +// if (startTop > 9) +// startTop -= 5; // if top space > 9, then allways leave blank 5 pixels on top (so . is not confused with '). +// topCropping = startTop; + +// for (int y = bmp.Height - 1; y > 3; y--) +// { +// int difference = 0; +// bool allTransparent = true; +// for (int x = 1; x < bmp.Width - 1; x++) +// { +// Color c = bmp.GetPixel(x, y); +// if (c.A != 0) +// { +// difference++; +// if (difference >= maxDifferentPixelsOnLine) +// { +// allTransparent = false; +// break; +// } +// } +// } +// if (allTransparent == false) +// return Copy(bmp, new Rectangle(0, startTop, bmp.Width - 1, y - startTop + 1)); +// } +// return bmp; +// } + +// public static List SplitVertical(Bitmap bmp) +// { // split into lines +// int startY = 0; +// int size = 0; +// var parts = new List(); +// for (int y = 0; y < bmp.Height; y++) +// { +// bool allTransparent = true; +// for (int x = 0; x < bmp.Width; x++) +// { +// Color c = bmp.GetPixel(x, y); +// if (c.A != 0) +// { +// allTransparent = false; +// break; +// } +// } +// if (allTransparent) +// { +// if (size > 2 && size < 6) +// { +// size++; // at least 5 pixels, like top of 'i' +// } +// else +// { +// if (size > 2) +// { +// Bitmap part = Copy(bmp, new Rectangle(0, startY, bmp.Width, size+1)); +//// part.Save("c:\\line_0_to_width.bmp"); +// parts.Add(new ImageSplitterItem(0, startY, part)); +//// bmp.Save("c:\\original.bmp"); +// } +// size = 0; +// startY = y; +// } +// } +// else +// { +// size++; +// } + +// } +// if (size > 2) +// { +// Bitmap part = Copy(bmp, new Rectangle(0, startY, bmp.Width, size+1)); +// parts.Add(new ImageSplitterItem(0, startY, part)); +// } +// return parts; +// } + +// public static List SplitVertical(Bitmap bmp, int lineMinHeight) +// { // split into lines +// int startY = 0; +// int size = 0; +// var parts = new List(); +// for (int y = 0; y < bmp.Height; y++) +// { +// bool allTransparent = true; +// for (int x = 0; x < bmp.Width; x++) +// { +// Color c = bmp.GetPixel(x, y); +// if (c.A != 0) +// { +// allTransparent = false; +// break; +// } +// } +// if (allTransparent) +// { +// if (size > 2 && size <= lineMinHeight) +// { +// size++; // at least 5 pixels, like top of 'i' +// } +// else +// { +// if (size > 2) +// { +// Bitmap part = Copy(bmp, new Rectangle(0, startY, bmp.Width, size + 1)); +// // part.Save("c:\\line_0_to_width.bmp"); +// parts.Add(new ImageSplitterItem(0, startY, part)); +// // bmp.Save("c:\\original.bmp"); +// } +// size = 0; +// startY = y; +// } +// } +// else +// { +// size++; +// } + +// } +// if (size > 2) +// { +// Bitmap part = Copy(bmp, new Rectangle(0, startY, bmp.Width, size + 1)); +// parts.Add(new ImageSplitterItem(0, startY, part)); +// } +// return parts; +// } + +// public static int IsBitmapsAlike(Bitmap bmp1, Bitmap bmp2) +// { +// int different = 0; +// int maxDiff = (int)(bmp1.Width * bmp1.Height / 5.0); + +// for (int x = 1; x < bmp1.Width; x++) +// { +// for (int y = 1; y < bmp1.Height; y++) +// { +// if (!IsColorClose(bmp1.GetPixel(x, y), bmp2.GetPixel(x, y), 20)) +// { +// different++; +// } +// } +// if (different > maxDiff) +// return different + 10; +// } +// return different; +// } + +// public static int IsBitmapsAlike(FastBitmap bmp1, Bitmap bmp2) +// { +// int different = 0; + +// for (int x = 1; x < bmp1.Width; x++) +// { +// for (int y = 1; y < bmp1.Height; y++) +// { +// Color c1 = bmp1.GetPixel(x, y); +// Color c2 = bmp1.GetPixel(x, y); +// if (!IsColorClose(c1, c2, 20)) +// different++; +// } +// } +// return different; +// } + +// private static List SplitHorizontal(ImageSplitterItem verticalItem, int xOrMorePixelsMakesSpace) +// { // split line into letters +// Bitmap bmp = verticalItem.NikseBitmap.GetBitmap(); +// var parts = new List(); +// int size = 0; +// int startX = 0; +// int lastEndX = 0; +// int y = 0; +// bool spaceJustAdded = false; + +// for (int x = 0; x < bmp.Width - 1; x++) +// { +// bool allTransparent = IsVerticalLineTransparent(bmp, ref y, x); + +// // check if line is transparent and cursive +// bool cursiveOk = false; +// int tempY = 0; +// if (allTransparent == false && +// size > 5 && +// y > 3 && +// x < bmp.Width-2 && +// !IsVerticalLineTransparent(bmp, ref tempY, x + 1)) +// { + +// //Add space? +// if (lastEndX > 0 && lastEndX + xOrMorePixelsMakesSpace < startX) +// { +// int cleanCount = 0; +// for (int j = lastEndX; j < startX; j++) +// { +// int y1 = j; +// if (IsVerticalLineTransparent2(bmp, ref y1, j)) +// cleanCount++; +// } +// if (cleanCount > 0 && !spaceJustAdded) +// { +// parts.Add(new ImageSplitterItem(" ")); +// spaceJustAdded = true; +// } +// } + +// var cursivePoints = new List(); + +// cursiveOk = IsCursiveVerticalLineTransparent(bmp, size, y, x, cursivePoints); + +// if (cursiveOk) +// { +// // make letter image +// int end = x + 1 - startX; +// if (startX > 0) +// { +// startX--; +// end++; +// } +// Bitmap b1 = Copy(bmp, new Rectangle(startX, 0, end, bmp.Height)); +//// b1.Save(@"d:\temp\cursive.bmp"); // just for debugging + +// // make non-black/transparent stuff from other letter transparent +// foreach (Point p in cursivePoints) +// { +// for (int fixY = p.Y; fixY < bmp.Height; fixY++) +// b1.SetPixel(p.X - startX, fixY, Color.Transparent); +// } + +// RemoveBlackBarRight(b1); +//// b1.Save(@"d:\temp\cursive-cleaned.bmp"); // just for debugging + +// // crop and save image +// int addY; +// b1 = CropTopAndBottom(b1, out addY); +// parts.Add(new ImageSplitterItem(startX, verticalItem.Y + addY, b1)); +// spaceJustAdded = false; +// size = 0; +// startX = x + 1; +// lastEndX = x; +// } +// } + +// if (!cursiveOk) +// { +// if (allTransparent) +// { +// if (size > 0) +// { +// if (size > 1) +// { +// //Add space? +// if (lastEndX > 0 && lastEndX + xOrMorePixelsMakesSpace < startX) +// { +// int cleanCount = 0; +// for (int j = lastEndX; j < startX; j++) +// { +// int y1=j; +// if (IsVerticalLineTransparent2(bmp, ref y1, j)) +// cleanCount++; +// } +// if (cleanCount > 2 && !spaceJustAdded) +// { +// parts.Add(new ImageSplitterItem(" ")); +// spaceJustAdded = true; +// } +// } + +// if (startX > 0) +// startX--; +// lastEndX = x; +// int end = x + 1 - startX; +// Bitmap part = Copy(bmp, new Rectangle(startX, 0, end, bmp.Height)); +// RemoveBlackBarRight(part); +// int addY; +// // part.Save("c:\\before" + startX.ToString() + ".bmp"); // just for debugging +// part = CropTopAndBottom(part, out addY); +// // part.Save("c:\\after" + startX.ToString() + ".bmp"); // just for debugging +// parts.Add(new ImageSplitterItem(startX, verticalItem.Y + addY, part)); +// spaceJustAdded = false; +//// part.Save(@"d:\temp\cursive.bmp"); // just for debugging +// } +// size = 0; +// } +// startX = x + 1; +// } +// else +// { +// size++; +// } +// } +// } + +// if (size > 0) +// { +// if (lastEndX > 0 && lastEndX + xOrMorePixelsMakesSpace < startX && !spaceJustAdded) +// parts.Add(new ImageSplitterItem(" ")); + +// if (startX > 0) +// startX--; +// lastEndX = bmp.Width-1; +// int end = lastEndX + 1 - startX; +// Bitmap part = Copy(bmp, new Rectangle(startX, 0, end, bmp.Height - 1)); +// int addY; +// part = CropTopAndBottom(part, out addY); +// parts.Add(new ImageSplitterItem(startX, verticalItem.Y + addY, part)); +// //part.Save(@"d:\temp\cursive.bmp"); // just for debugging +// } +// return parts; +// } + +// private static void RemoveBlackBarRight(Bitmap bmp) +// { +// int xRemoveBlackBar = bmp.Width-1; +// for (int yRemoveBlackBar = 0; yRemoveBlackBar < bmp.Height; yRemoveBlackBar++) +// { +// Color c = bmp.GetPixel(xRemoveBlackBar, yRemoveBlackBar); +// if (c.A == 0 || IsColorClose(c, Color.Black, 280)) +// { +// if (bmp.GetPixel(xRemoveBlackBar - 1, yRemoveBlackBar).A == 0) +// bmp.SetPixel(xRemoveBlackBar, yRemoveBlackBar, Color.Transparent); +// } +// } +// } + +// private static bool IsCursiveVerticalLineTransparent(Bitmap bmp, int size, int y, int x, List cursivePoints) +// { +// bool cursiveOk = true; +// int newY = y; +// int newX = x; +// while (cursiveOk && newY < bmp.Height - 1) +// { +// Color c0 = bmp.GetPixel(newX, newY); +// if (c0.A == 0 || IsColorClose(c0, Color.Black, 280)) +// { +// newY++; +// } +// else +// { +// Color c1 = bmp.GetPixel(newX - 1, newY - 1); +// Color c2 = bmp.GetPixel(newX - 1, newY); +// if ((c1.A == 0 || IsColorClose(c1, Color.Black, 280)) && // still dark color... +// (c2.A == 0 || IsColorClose(c2, Color.Black, 280))) +// { +// cursivePoints.Add(new Point(newX, newY)); +// if (newX > 1) +// newX--; +// else +// cursiveOk = false; + +// newY++; +// } +// else +// { +// cursiveOk = false; +// } +// } + +// if (newX < x - size) +// cursiveOk = false; +// } +// return cursiveOk; +// } + +// private static bool IsVerticalLineTransparent(Bitmap bmp, ref int y, int x) +// { +// bool allTransparent = true; +// for (y = 0; y < bmp.Height - 1; y++) +// { +// Color c = bmp.GetPixel(x, y); +// if (c.A == 0 || //c.ToArgb() == transparentColor.ToArgb() || +// IsColorClose(c, Color.Black, 280)) // still dark color... +// { +// } +// else +// { +// allTransparent = false; +// break; +// } +// } +// return allTransparent; +// } + +// private static bool IsVerticalLineTransparent2(Bitmap bmp, ref int y, int x) +// { +// bool allTransparent = true; +// for (y = 0; y < bmp.Height - 1; y++) +// { +// Color c = bmp.GetPixel(x, y); +// if (c.A == 0) // still dark color... +// { +// } +// else +// { +// allTransparent = false; +// break; +// } +// } +// return allTransparent; +// } + +// public static List SplitBitmapToLetters(Bitmap bmp, int xOrMorePixelsMakesSpace, bool rightToLeft, bool topToBottom) +// { +// var list = new List(); + +// // split into seperate lines +// List verticalBitmaps = SplitVertical(bmp, xOrMorePixelsMakesSpace); + +// if (!topToBottom) +// verticalBitmaps.Reverse(); + +// // split into letters +// int lineCount = 0; +// foreach (ImageSplitterItem b in verticalBitmaps) +// { +// if (lineCount > 0) +// list.Add(new ImageSplitterItem(Environment.NewLine)); +// var line = new List(); +// foreach (ImageSplitterItem item in SplitHorizontal(b, xOrMorePixelsMakesSpace)) +// { +// item.ParentY = item.Y; +// line.Add(item); +// } +// if (rightToLeft) +// line.Reverse(); +// foreach (ImageSplitterItem item in line) +// list.Add(item); +// lineCount++; +// } + +// return list; +// } + +// public static List SplitBitmapToLetters(List verticalBitmaps, int xOrMorePixelsMakesSpace, bool rightToLeft, bool topToBottom) +// { +// var list = new List(); +// if (!topToBottom) +// verticalBitmaps.Reverse(); + +// // split into letters +// int lineCount = 0; +// foreach (ImageSplitterItem b in verticalBitmaps) +// { +// if (lineCount > 0) +// list.Add(new ImageSplitterItem(Environment.NewLine)); +// var line = new List(); +// foreach (ImageSplitterItem item in SplitHorizontal(b, xOrMorePixelsMakesSpace)) +// { +// item.ParentY = item.Y; +// line.Add(item); +// } +// if (rightToLeft) +// line.Reverse(); +// foreach (ImageSplitterItem item in line) +// list.Add(item); +// lineCount++; +// } + +// return list; +// } + +// internal static unsafe int IsBitmapsAlike(NikseBitmap bmp1, NikseBitmap bmp2) +// { +// int different = 0; +// int maxDiff = (int)(bmp1.Width * bmp1.Height / 5.0); + +// for (int x = 1; x < bmp1.Width; x++) +// { +// for (int y = 1; y < bmp1.Height; y++) +// { +// if (!IsColorClose(bmp1.GetPixel(x, y), bmp2.GetPixel(x, y), 20)) +// { +// different++; +// } +// } +// if (different > maxDiff) +// return different + 10; +// } +// return different; +// } + +// internal static unsafe int IsBitmapsAlike(ManagedBitmap bmp1, NikseBitmap bmp2) +// { +// int different = 0; +// int maxDiff = (int)(bmp1.Width * bmp1.Height / 5.0); + +// for (int x = 1; x < bmp1.Width; x++) +// { +// for (int y = 1; y < bmp1.Height; y++) +// { +// if (!IsColorClose(bmp1.GetPixel(x, y), bmp2.GetPixel(x, y), 20)) +// { +// different++; +// } +// } +// if (different > maxDiff) +// return different + 10; +// } +// return different; +// } + +// internal static unsafe int IsBitmapsAlike(ManagedBitmap bmp1, Bitmap bmp2) +// { +// int different = 0; +// int maxDiff = (int)(bmp1.Width * bmp1.Height / 5.0); + +// for (int x = 1; x < bmp1.Width; x++) +// { +// for (int y = 1; y < bmp1.Height; y++) +// { +// if (!IsColorClose(bmp1.GetPixel(x, y), bmp2.GetPixel(x, y), 20)) +// { +// different++; +// } +// } +// if (different > maxDiff) +// return different + 10; +// } +// return different; +// } + +// internal static unsafe int IsBitmapsAlike(NikseBitmap bmp1, ManagedBitmap bmp2) +// { +// int different = 0; +// int maxDiff = (int)(bmp1.Width * bmp1.Height / 5.0); + +// for (int x = 1; x < bmp1.Width; x++) +// { +// for (int y = 1; y < bmp1.Height; y++) +// { +// if (!IsColorClose(bmp1.GetPixel(x, y), bmp2.GetPixel(x, y), 20)) +// { +// different++; +// } +// } +// if (different > maxDiff) +// return different + 10; +// } +// return different; +// } + +// internal static unsafe int IsBitmapsAlike(Bitmap bmp1, NikseBitmap bmp2) +// { +// int different = 0; +// int maxDiff = (int)(bmp1.Width * bmp1.Height / 5.0); +// NikseBitmap nbmp1 = new NikseBitmap(bmp1); + +// for (int x = 1; x < bmp1.Width; x++) +// { +// for (int y = 1; y < bmp1.Height; y++) +// { +// if (!IsColorClose(nbmp1.GetPixel(x, y), bmp2.GetPixel(x, y), 20)) +// { +// different++; +// } +// } +// if (different > maxDiff) +// return different + 10; +// } +// return different; +// } + +// internal static unsafe int IsBitmapsAlike(NikseBitmap bmp1, Bitmap bmp2) +// { +// int different = 0; +// int maxDiff = (int)(bmp1.Width * bmp1.Height / 5.0); + +// for (int x = 1; x < bmp1.Width; x++) +// { +// for (int y = 1; y < bmp1.Height; y++) +// { +// if (!IsColorClose(bmp1.GetPixel(x, y), bmp2.GetPixel(x, y), 20)) +// { +// different++; +// } +// } +// if (different > maxDiff) +// return different + 10; +// } +// return different; +// } +// } +//} diff --git a/libse/ImageSplitterItem.cs b/libse/ImageSplitterItem.cs new file mode 100644 index 000000000..20137f49b --- /dev/null +++ b/libse/ImageSplitterItem.cs @@ -0,0 +1,27 @@ +namespace Nikse.SubtitleEdit.Core +{ + public class ImageSplitterItem + { + public int X { get; set; } + public int Y { get; set; } + public int ParentY { get; set; } + public NikseBitmap NikseBitmap { get; set; } + public string SpecialCharacter { get; set; } + + public ImageSplitterItem(int x, int y, NikseBitmap bitmap) + { + X = x; + Y = y; + NikseBitmap = bitmap; + SpecialCharacter = null; + } + + public ImageSplitterItem(string specialCharacter) + { + X = 0; + Y = 0; + SpecialCharacter = specialCharacter; + NikseBitmap = null; + } + } +} diff --git a/libse/Language.cs b/libse/Language.cs new file mode 100644 index 000000000..85e95076d --- /dev/null +++ b/libse/Language.cs @@ -0,0 +1,2501 @@ +using System; +using System.IO; +using System.Text; +using System.Xml.Serialization; + +namespace Nikse.SubtitleEdit.Core +{ + public class Language + { + [XmlAttribute("Name")] + public string Name; + public LanguageStructure.General General; + public LanguageStructure.About About; + public LanguageStructure.AddToNames AddToNames; + public LanguageStructure.AddToOcrReplaceList AddToOcrReplaceList; + public LanguageStructure.AddToUserDictionary AddToUserDictionary; + public LanguageStructure.AddWaveform AddWaveform; + public LanguageStructure.AddWaveformBatch AddWaveformBatch; + public LanguageStructure.AdjustDisplayDuration AdjustDisplayDuration; + public LanguageStructure.ApplyDurationLimits ApplyDurationLimits; + public LanguageStructure.AutoBreakUnbreakLines AutoBreakUnbreakLines; + public LanguageStructure.BatchConvert BatchConvert; + public LanguageStructure.Beamer Beamer; + public LanguageStructure.ChangeCasing ChangeCasing; + public LanguageStructure.ChangeCasingNames ChangeCasingNames; + public LanguageStructure.ChangeFrameRate ChangeFrameRate; + public LanguageStructure.ChangeSpeedInPercent ChangeSpeedInPercent; + public LanguageStructure.CheckForUpdates CheckForUpdates; + public LanguageStructure.ChooseAudioTrack ChooseAudioTrack; + public LanguageStructure.ChooseEncoding ChooseEncoding; + public LanguageStructure.ChooseLanguage ChooseLanguage; + public LanguageStructure.ColorChooser ColorChooser; + public LanguageStructure.ColumnPaste ColumnPaste; + public LanguageStructure.CompareSubtitles CompareSubtitles; + public LanguageStructure.DCinemaProperties DCinemaProperties; + public LanguageStructure.DurationsBridgeGaps DurationsBridgeGaps; + public LanguageStructure.DvdSubRip DvdSubRip; + public LanguageStructure.DvdSubRipChooseLanguage DvdSubRipChooseLanguage; + public LanguageStructure.EbuSaveOptions EbuSaveOptions; + public LanguageStructure.EffectKaraoke EffectKaraoke; + public LanguageStructure.EffectTypewriter EffectTypewriter; + public LanguageStructure.ExportCustomText ExportCustomText; + public LanguageStructure.ExportCustomTextFormat ExportCustomTextFormat; + public LanguageStructure.ExportPngXml ExportPngXml; + public LanguageStructure.ExportText ExportText; + public LanguageStructure.ExtractDateTimeInfo ExtractDateTimeInfo; + public LanguageStructure.FindDialog FindDialog; + public LanguageStructure.FindSubtitleLine FindSubtitleLine; + public LanguageStructure.FixCommonErrors FixCommonErrors; + public LanguageStructure.GetDictionaries GetDictionaries; + public LanguageStructure.GetTesseractDictionaries GetTesseractDictionaries; + public LanguageStructure.GoogleTranslate GoogleTranslate; + public LanguageStructure.GoogleOrMicrosoftTranslate GoogleOrMicrosoftTranslate; + public LanguageStructure.GoToLine GoToLine; + public LanguageStructure.ImportImages ImportImages; + public LanguageStructure.ImportSceneChanges ImportSceneChanges; + public LanguageStructure.ImportText ImportText; + public LanguageStructure.Interjections Interjections; + public LanguageStructure.JoinSubtitles JoinSubtitles; + public LanguageStructure.Main Main; + public LanguageStructure.MatroskaSubtitleChooser MatroskaSubtitleChooser; + public LanguageStructure.MeasurementConverter MeasurementConverter; + public LanguageStructure.MergeDoubleLines MergeDoubleLines; + public LanguageStructure.MergeShortLines MergedShortLines; + public LanguageStructure.MergeTextWithSameTimeCodes MergeTextWithSameTimeCodes; + public LanguageStructure.ModifySelection ModifySelection; + public LanguageStructure.MultipleReplace MultipleReplace; + public LanguageStructure.NetworkChat NetworkChat; + public LanguageStructure.NetworkJoin NetworkJoin; + public LanguageStructure.NetworkLogAndInfo NetworkLogAndInfo; + public LanguageStructure.NetworkStart NetworkStart; + public LanguageStructure.OpenVideoDvd OpenVideoDvd; + public LanguageStructure.PluginsGet PluginsGet; + public LanguageStructure.RegularExpressionContextMenu RegularExpressionContextMenu; + public LanguageStructure.RemoveTextFromHearImpaired RemoveTextFromHearImpaired; + public LanguageStructure.ReplaceDialog ReplaceDialog; + public LanguageStructure.RestoreAutoBackup RestoreAutoBackup; + public LanguageStructure.SeekSilence SeekSilence; + public LanguageStructure.SetMinimumDisplayTimeBetweenParagraphs SetMinimumDisplayTimeBetweenParagraphs; + public LanguageStructure.SetSyncPoint SetSyncPoint; + public LanguageStructure.Settings Settings; + public LanguageStructure.SetVideoOffset SetVideoOffset; + public LanguageStructure.ShowEarlierLater ShowEarlierLater; + public LanguageStructure.ShowHistory ShowHistory; + public LanguageStructure.SpellCheck SpellCheck; + public LanguageStructure.Split Split; + public LanguageStructure.SplitLongLines SplitLongLines; + public LanguageStructure.SplitSubtitle SplitSubtitle; + public LanguageStructure.StartNumberingFrom StartNumberingFrom; + public LanguageStructure.Statistics Statistics; + public LanguageStructure.SubStationAlphaProperties SubStationAlphaProperties; + public LanguageStructure.SubStationAlphaStyles SubStationAlphaStyles; + public LanguageStructure.PointSync PointSync; + public LanguageStructure.TransportStreamSubtitleChooser TransportStreamSubtitleChooser; + public LanguageStructure.UnknownSubtitle UnknownSubtitle; + public LanguageStructure.VisualSync VisualSync; + public LanguageStructure.VobSubEditCharacters VobSubEditCharacters; + public LanguageStructure.VobSubOcr VobSubOcr; + public LanguageStructure.VobSubOcrCharacter VobSubOcrCharacter; + public LanguageStructure.VobSubOcrCharacterInspect VobSubOcrCharacterInspect; + public LanguageStructure.VobSubOcrNewFolder VobSubOcrNewFolder; + public LanguageStructure.VobSubOcrSetItalicFactor VobSubOcrSetItalicFactor; + public LanguageStructure.Waveform Waveform; + public LanguageStructure.WaveformGenerateTimeCodes WaveformGenerateTimeCodes; + public LanguageStructure.WebVttNewVoice WebVttNewVoice; + + public Language() + { + Name = "English"; + + General = new LanguageStructure.General + { + Title = "Subtitle Edit", + Version = "3.4", + TranslatedBy = " ", + CultureName = "en-US", + HelpFile = string.Empty, + Ok = "&OK", + Cancel = "C&ancel", + Apply = "Apply", + None = "None", + All = "All", + Preview = "Preview", + SubtitleFiles = "Subtitle files", + AllFiles = "All files", + VideoFiles = "Video files", + AudioFiles = "Audio files", + OpenSubtitle = "Open subtitle...", + OpenVideoFile = "Open video file...", + OpenVideoFileTitle = "Open video file...", + NoVideoLoaded = "No video loaded", + VideoInformation = "Video info", + PositionX = "Position / duration: {0}", + StartTime = "Start time", + EndTime = "End time", + Duration = "Duration", + NumberSymbol = "#", + Number = "Number", + Text = "Text", + HourMinutesSecondsMilliseconds = "Hour:min:sec:ms", + Bold = "Bold", + Italic = "Italic", + Underline = "Underline", + Visible = "Visible", + FrameRate = "Frame rate", + Name = "Name", + SingleLineLengths = "Single line length:", + TotalLengthX = "Total length: {0}", + TotalLengthXSplitLine = "Total length: {0} (split line!)", + SplitLine = "Split line!", + NotAvailable = "N/A", + FileNameXAndSize = "File name: {0} ({1})", + ResolutionX = "Resolution: {0}", + FrameRateX = "Frame rate: {0:0.0###}", + TotalFramesX = "Total frames: {0:#,##0.##}", + VideoEncodingX = "Video encoding: {0}", + OverlapPreviousLineX = "Overlap prev line ({0:#,##0.###})", + OverlapX = "Overlap ({0:#,##0.###})", + OverlapNextX = "Overlap next ({0:#,##0.###})", + Negative = "Negative", + RegularExpressionIsNotValid = "Regular expression is not valid!", + SubtitleSaved = "Subtitle saved", + CurrentSubtitle = "Current subtitle", + OriginalText = "Original text", + OpenOriginalSubtitleFile = "Open original subtitle file...", + PleaseWait = "Please wait...", + SessionKey = "Session key", + UserName = "Username", + UserNameAlreadyInUse = "Username already in use", + WebServiceUrl = "Webservice URL", + IP = "IP", + VideoWindowTitle = "Video - {0}", + AudioWindowTitle = "Audio - {0}", + ControlsWindowTitle = "Controls - {0}", + Advanced = "Advanced", + Style = "Style", + StyleLanguage = "Style / Language", + Character = "Character", + Class = "Class", + GeneralText = "General", + LineNumber = "Line#", + Before = "Before", + After = "After", + Size = "Size", + }; + + About = new LanguageStructure.About + { + Title = "About Subtitle Edit", + AboutText1 = "Subtitle Edit is Free Software under the GNU Public License." + Environment.NewLine + + "You may distribute, modify and use it freely." + Environment.NewLine + + Environment.NewLine + + "C# source code is available on https://github.com/SubtitleEdit/subtitleedit" + Environment.NewLine + + Environment.NewLine + + "Visit www.nikse.dk for the latest version." + Environment.NewLine + + Environment.NewLine + + "Suggestions are very welcome." + Environment.NewLine + + Environment.NewLine + + "Email: mailto:nikse.dk@gmail.com", + }; + + AddToNames = new LanguageStructure.AddToNames + { + Title = "Add to names/etc list", + Description = "Add to names/noise list (case sensitive)", + }; + + AddToOcrReplaceList = new LanguageStructure.AddToOcrReplaceList + { + Title = "Add to OCR replace list", + Description = "Add pair to OCR replace list (case sensitive)", + }; + + AddToUserDictionary = new LanguageStructure.AddToUserDictionary + { + Title = "Add to user dictionary", + Description = "Add word to user dictionary (not case sensitive)", + }; + + AddWaveform = new LanguageStructure.AddWaveform + { + Title = "Generate waveform data", + GenerateWaveformData = "Generate waveform data", + SourceVideoFile = "Source video file:", + PleaseWait = "This may take a few minutes - please wait", + VlcMediaPlayerNotFoundTitle = "VLC media player not found", + VlcMediaPlayerNotFound = "Subtitle Edit needs VLC media player 1.1.x or newer for extracting audio data.", + GoToVlcMediaPlayerHomePage = "Do you want to go to the VLC media player home page?", + GeneratingPeakFile = "Generating peak file...", + GeneratingSpectrogram = "Generating spectrogram...", + ExtractingSeconds = "Extracting audio: {0:0.0} seconds", + ExtractingMinutes = "Extracting audio: {0}.{1:00} minutes", + WaveFileNotFound = "Could not find extracted wave file!" + Environment.NewLine + + "This feature requires VLC media player 1.1.x or newer ({0}-bit)." + Environment.NewLine + + Environment.NewLine + + "Command line: {1} {2}", + WaveFileMalformed = "{0} was unable to extract audio data to wave file!" + Environment.NewLine + + Environment.NewLine + + "Command line: {1} {2}" + Environment.NewLine + + Environment.NewLine + + "Note: Do check free disk space.", + LowDiskSpace = "LOW DISK SPACE!", + FreeDiskSpace = "{0} free", + }; + + AddWaveformBatch = new LanguageStructure.AddWaveformBatch + { + Title = "Batch generate waveform data", + Calculating = "Calculating...", + ExtractingAudio = "Extracting audio...", + Done = "Done", + Error = "Error", + }; + + AdjustDisplayDuration = new LanguageStructure.AdjustDisplayDuration + { + Title = "Adjust durations", + AdjustVia = "Adjust via", + AddSeconds = "Add seconds", + SetAsPercent = "Set as percent of duration", + Percent = "Percent", + Recalculate = "Recalculate", + Seconds = "Seconds", + Note = "Note: Display time will not overlap start time of next text", + PleaseSelectAValueFromTheDropDownList = "Please select a value from the dropdown list", + PleaseChoose = " - Please choose - ", + }; + + ApplyDurationLimits = new LanguageStructure.ApplyDurationLimits + { + Title = "Apply duration limits", + FixesAvailable = "Fixes available: {0}", + UnableToFix = "Unable to fix: {0}", + }; + + AutoBreakUnbreakLines = new LanguageStructure.AutoBreakUnbreakLines + { + TitleAutoBreak = "Auto balance selected lines", + TitleUnbreak = "Remove line breaks from selected lines", + LinesFoundX = "Lines found: {0}", + OnlyBreakLinesLongerThan = "Only break lines longer than", + OnlyUnbreakLinesLongerThan = "Only un-break lines longer than", + }; + + BatchConvert = new LanguageStructure.BatchConvert + { + Title = "Batch convert", + Input = "Input", + Output = "Output", + ChooseOutputFolder = "Choose output folder", + ConvertOptions = "Convert options", + RemoveTextForHI = "Remove text for HI", + InputDescription = "Input files (browse or drag-n-drop)", + Convert = "Convert", + OverwriteExistingFiles = "Overwrite existing files", + RedoCasing = "Redo casing", + RemoveFormatting = "Remove formatting tags", + Status = "Status", + Style = "Style...", + NothingToConvert = "Nothing to convert!", + PleaseChooseOutputFolder = "Please choose output folder", + NotConverted = "Failed", + Converted = "Converted", + ConvertedX = "Converted ({0})", + Settings = "Settings", + SplitLongLines = "Split long lines", + AutoBalance = "Auto balance lines", + OverwriteOriginalFiles = "Overwrite original files (new extension if format is changed)", + ScanFolder = "Scan folder...", + ScanningFolder = "Scanning {0} and subfolders for subtitle files...", + Recursive = "Include sub folders", + SetMinMsBetweenSubtitles = "Set min. milliseconds between subtitles", + PlainText = "Plain text", + Ocr = "OCR...", + Filter = "Filter", + FilterSkipped = "Skipped by filter", + FilterSrtNoUtf8BOM = "SubRip (.srt) files without UTF-8 BOM header", + FilterMoreThanTwoLines = "More than two lines in one subtitle", + FilterContains = "Text contains...", + FixCommonErrorsErrorX = "Fix common errors: {0}", + MultipleReplaceErrorX = "Multiple replace: {0}", + AutoBalanceErrorX = "Auto balance: {0}", + }; + + Beamer = new LanguageStructure.Beamer + { + Title = "Beamer", + }; + + ChangeCasing = new LanguageStructure.ChangeCasing + { + Title = "Change casing", + ChangeCasingTo = "Change casing to", + NormalCasing = "Normal casing. Sentences begin with uppercase letter.", + FixNamesCasing = @"Fix names casing (via Dictionaries\NamesEtc.xml)", + FixOnlyNamesCasing = @"Fix only names casing (via Dictionaries\NamesEtc.xml)", + OnlyChangeAllUppercaseLines = "Only change all upper case lines.", + AllUppercase = "ALL UPPERCASE", + AllLowercase = "all lowercase", + }; + + ChangeCasingNames = new LanguageStructure.ChangeCasingNames + { + Title = "Change casing - Names", + NamesFoundInSubtitleX = "Names found in subtitle: {0}", + Enabled = "Enabled", + Name = "Name", + LinesFoundX = "Lines found: {0}", + }; + + ChangeFrameRate = new LanguageStructure.ChangeFrameRate + { + Title = "Change frame rate", + ConvertFrameRateOfSubtitle = "Convert frame rate of subtitle", + FromFrameRate = "From frame rate", + ToFrameRate = "To frame rate", + FrameRateNotCorrect = "Frame rate is not correct", + FrameRateNotChanged = "Frame rate is the same - nothing to convert", + }; + + ChangeSpeedInPercent = new LanguageStructure.ChangeSpeedInPercent + { + Title = "Adjust speed in percent", + Info = "Change speed of subtitle in percent", + Custom = "Custom", + ToDropFrame = "To drop frame", + FromDropFrame = "From drop frame", + }; + + CheckForUpdates = new LanguageStructure.CheckForUpdates + { + Title = "Check for updates", + CheckingForUpdates = "Checking for updates...", + CheckingForUpdatesFailedX = "Checking for updates failed: {0}", + CheckingForUpdatesNoneAvailable = "You're using the latest version of Subtitle Edit :)", + CheckingForUpdatesNewVersion = "New version available!", + InstallUpdate = "Go to download page", + NoUpdates = "Don't update", + }; + + ChooseAudioTrack = new LanguageStructure.ChooseAudioTrack + { + Title = "Choose audio track", + }; + + ChooseEncoding = new LanguageStructure.ChooseEncoding + { + Title = "Choose encoding", + CodePage = "Code page", + DisplayName = "Display name", + PleaseSelectAnEncoding = "Please select an encoding", + }; + + ChooseLanguage = new LanguageStructure.ChooseLanguage + { + Title = "Choose language", + Language = "Language", + }; + + ColorChooser = new LanguageStructure.ColorChooser + { + Title = "Choose color", + Red = "Red", + Green = "Green", + Blue = "Blue", + Alpha = "Alpha", + }; + + ColumnPaste = new LanguageStructure.ColumnPaste + { + Title = "Column paste", + ChooseColumn = "Choose column", + OverwriteShiftCellsDown = "Overwrite/Shift cells down", + Overwrite = "Overwrite", + ShiftCellsDown = "Shift cells down", + TimeCodesOnly = "Time codes only", + TextOnly = "Text only", + OriginalTextOnly = "Original text only", + }; + + CompareSubtitles = new LanguageStructure.CompareSubtitles + { + Title = "Compare subtitles", + PreviousDifference = "&Previous difference", + NextDifference = "&Next difference", + SubtitlesNotAlike = "Subtitles have no similarities", + XNumberOfDifference = "Number of differences: {0}", + XNumberOfDifferenceAndPercentChanged = "Number of differences: {0} ({1}% of words changed)", + XNumberOfDifferenceAndPercentLettersChanged = "Number of differences: {0} ({1}% of letters changed)", + ShowOnlyDifferences = "Show only differences", + IgnoreLineBreaks = "Ignore line breaks", + OnlyLookForDifferencesInText = "Only look for differences in text", + CannotCompareWithImageBasedSubtitles = "Cannot compare with image based subtitles", + }; + + DCinemaProperties = new LanguageStructure.DCinemaProperties + { + Title = "D-Cinema properties (interop)", + TitleSmpte = "D-Cinema properties (SMPTE)", + SubtitleId = "Subtitle ID", + GenerateId = "Generate ID", + MovieTitle = "Movie title", + ReelNumber = "Reel number", + Language = "Language", + IssueDate = "Issue date", + EditRate = "Edit rate", + TimeCodeRate = "Time code rate", + StartTime = "Start time", + Font = "Font", + FontId = "ID", + FontUri = "URI", + FontColor = "Color", + FontEffect = "Effect", + FontEffectColor = "Effect color", + FontSize = "Size", + TopBottomMargin = "Top/bottom margin", + FadeUpTime = "Fade up time", + FadeDownTime = "Fade down time", + ZPosition = "Z-position", + ZPositionHelp = "Positive numbers moves text away, negative numbers moves text closer, if z-position is zero then it's 2D", + ChooseColor = "Choose color...", + Generate = "Generate", + }; + + DurationsBridgeGaps = new LanguageStructure.DurationsBridgeGaps + { + Title = "Bridge small gaps in durations", + GapsBridgedX = "Number of small gaps bridged: {0}", + GapToNext = "Gap to next in seconds", + BridgeGapsSmallerThanXPart1 = "Bridge gaps smaller than", + BridgeGapsSmallerThanXPart2 = "milliseconds", + MinMillisecondsBetweenLines = "Min. milliseconds between lines", + ProlongEndTime = "Previous text takes all gap time", + DivideEven = "Texts divide gap time", + }; + + DvdSubRip = new LanguageStructure.DvdSubRip + { + Title = "Rip subtitles from IFO/VOBs (DVD)", + DvdGroupTitle = "DVD files/info", + IfoFile = "IFO file", + IfoFiles = "IFO files", + VobFiles = "VOB files", + Add = "Add...", + Remove = "Remove", + Clear = "Clear", + MoveUp = "Move up", + MoveDown = "Move down", + Languages = "Languages", + PalNtsc = "PAL/NTSC", + Pal = "PAL (25fps)", + Ntsc = "NTSC (29.97fps)", + StartRipping = "Start ripping", + Abort = "Abort", + AbortedByUser = "Aborted by user", + ReadingSubtitleData = "Reading subtitle data...", + RippingVobFileXofYZ = "Ripping vob file {1} of {2}: {0}", + WrongIfoType = "IFO type is '{0}' and not 'DVDVIDEO-VTS'.{1}Try another file than {2}", + }; + + DvdSubRipChooseLanguage = new LanguageStructure.DvdSubRipChooseLanguage + { + Title = "Choose language", + ChooseLanguageStreamId = "Choose language (stream-id)", + UnknownLanguage = "Unknown language", + SubtitleImageXofYAndWidthXHeight = "Subtitle image {0}/{1} - {2}x{3}", + SubtitleImage = "Subtitle image", + }; + + EbuSaveOptions = new LanguageStructure.EbuSaveOptions + { + Title = "EBU save options", + GeneralSubtitleInformation = "General subtitle information", + CodePageNumber = "Code page number", + DiskFormatCode = "Disk format code", + DisplayStandardCode = "Display standard code", + CharacterCodeTable = "Character table", + LanguageCode = "Language code", + OriginalProgramTitle = "Original program title", + OriginalEpisodeTitle = "Original episode title", + TranslatedProgramTitle = "Translated program title", + TranslatedEpisodeTitle = "Translated episode title", + TranslatorsName = "Translators name", + SubtitleListReferenceCode = "Subtitle list reference code", + CountryOfOrigin = "Country of origin", + TimeCodeStatus = "Time code status", + TimeCodeStartOfProgramme = "Time code: Start of programme", + RevisionNumber = "Revision number", + MaxNoOfDisplayableChars = "Max# of chars per row", + MaxNumberOfDisplayableRows = "Max# of rows", + DiskSequenceNumber = "Disk sequence number", + TotalNumberOfDisks = "Total number of disks", + Import = "Import...", + TextAndTimingInformation = "Text and timing information", + JustificationCode = "Justification code", + Errors = "Errors", + ErrorsX = "Errors: {0}", + MaxLengthError = "Line {0} exceeds max length ({1}) by {2}: {3}", + TextUnchangedPresentation = "Unchanged presentation", + TextLeftJustifiedText = "Left justified text", + TextCenteredText = "Centered text", + TextRightJustifiedText = "Right justified text", + }; + + EffectKaraoke = new LanguageStructure.EffectKaraoke + { + Title = "Karaoke effect", + ChooseColor = "Choose color:", + TotalMilliseconds = "Total milliseconds:", + EndDelayInMilliseconds = "End delay in milliseconds:" + }; + + EffectTypewriter = new LanguageStructure.EffectTypewriter + { + Title = "Typewriter effect", + TotalMilliseconds = "Total milliseconds:", + EndDelayInMilliseconds = "End delay in milliseconds:" + }; + + ExportCustomText = new LanguageStructure.ExportCustomText + { + Title = "Export custom text format", + Formats = "Formats", + New = "New", + Edit = "Edit", + Delete = "Delete", + SaveAs = "S&ave as...", + SaveSubtitleAs = "Save subtitle as...", + SubtitleExportedInCustomFormatToX = "Subtitle exported in custom format to: {0}", + }; + + ExportCustomTextFormat = new LanguageStructure.ExportCustomTextFormat + { + Title = "Custom text format template", + Template = "Template", + Header = "Header", + TextLine = "Text line (paragraph)", + TimeCode = "Time code", + NewLine = "New line", + Footer = "Footer", + DoNotModify = "[Do not modify]", + }; + + ExportPngXml = new LanguageStructure.ExportPngXml + { + Title = "Export BDN XML/PNG", + ImageSettings = "Image settings", + SimpleRendering = "Simple rendering", + AntiAliasingWithTransparency = "Anti-aliasing with transparency", + Text3D = "3D", + SideBySide3D = "Half-side-by-side", + HalfTopBottom3D = "Half-Top/Bottom", + Depth = "Depth", + BorderColor = "Border color", + BorderWidth = "Border width", + BorderStyle = "Border style", + BorderStyleOneBox = "One box", + BorderStyleBoxForEachLine = "Box for each line", + BorderStyleNormalWidthX = "Normal, width={0}", + ShadowColor = "Shadow color", + ShadowWidth = "Shadow width", + Transparency = "Alpha", + ImageFormat = "Image format", + FullFrameImage = "Full frame image", + ExportAllLines = "Export all lines...", + FontColor = "Font color", + FontFamily = "Font family", + FontSize = "Font size", + XImagesSavedInY = "{0} images saved in {1}", + VideoResolution = "Video res", + Align = "Align", + Left = "Left", + Center = "Center", + Right = "Right", + BottomMargin = "Bottom margin", + LeftRightMargin = "Left/right margin", + SaveBluRraySupAs = "Choose Blu-ray sup file name", + SaveVobSubAs = "Choose VobSub file name", + SaveFabImageScriptAs = "Choose FAB image script file name", + SaveDvdStudioProStlAs = "Choose DVD Studio Pro STL file name", + SaveDigitalCinemaInteropAs = "Choose Digital Cinema Interop file name", + SavePremiereEdlAs = "Choose Premiere EDL file name", + SaveFcpAs = "Choose Final Cut Pro xml file name", + SaveDostAs = "Choose DoStudio dost file name", + SomeLinesWereTooLongX = "Some lines were too long:" + Environment.NewLine + "{0}", + LineHeight = "Line height", + BoxSingleLine = "Box - single line", + BoxMultiLine = "Box - multi line", + Forced = "Forced", + ChooseBackgroundColor = "Choose background color", + SaveImageAs = "Save image as...", + }; + + ExportText = new LanguageStructure.ExportText + { + Title = "Export text", + Preview = "Preview", + ExportOptions = "Export options", + FormatText = "Format text", + None = "None", + MergeAllLines = "Merge all lines", + UnbreakLines = "Unbreak lines", + RemoveStyling = "Remove styling", + ShowLineNumbers = "Show line numbers", + AddNewLineAfterLineNumber = "Add new line after line number", + ShowTimeCode = "Show time code", + AddNewLineAfterTimeCode = "Add new line after time code", + AddNewLineAfterTexts = "Add new line after text", + AddNewLineBetweenSubtitles = "Add new line between subtitles", + TimeCodeFormat = "Time code format", + Srt = ".srt", + Milliseconds = "Milliseconds", + HHMMSSFF = "HH:MM:SS:FF", + TimeCodeSeparator = "Time code separator", + }; + + ExtractDateTimeInfo = new LanguageStructure.ExtractDateTimeInfo + { + Title = "Generate time as text", + OpenVideoFile = "Choose video file to extract date/time info from", + StartFrom = "Start from", + DateTimeFormat = "Date/time format", + Example = "Example", + GenerateSubtitle = "&Generate subtitle", + }; + + FindDialog = new LanguageStructure.FindDialog + { + Title = "Find", + Find = "Find", + Normal = "&Normal", + CaseSensitive = "&Case sensitive", + RegularExpression = "Regular e&xpression", + }; + + FindSubtitleLine = new LanguageStructure.FindSubtitleLine + { + Title = "Find subtitle line", + Find = "&Find", + FindNext = "Find &next", + }; + + FixCommonErrors = new LanguageStructure.FixCommonErrors + { + Title = "Fix common errors", + Step1 = "Step 1/2 - Choose which errors to fix", + WhatToFix = "What to fix", + Example = "Example", + SelectAll = "Select all", + InverseSelection = "Invert selection", + Back = "< &Back", + Next = "&Next >", + Step2 = "Step 2/2 - Verify fixes", + Fixes = "Fixes", + Log = "Log", + Function = "Function", + RemovedEmptyLine = "Remove empty line", + RemovedEmptyLineAtTop = "Remove empty line at top", + RemovedEmptyLineAtBottom = "Remove empty line at bottom", + RemovedEmptyLinesUnsedLineBreaks = "Remove empty lines/unused line breaks", + EmptyLinesRemovedX = "Empty lines removed: {0}", + FixOverlappingDisplayTimes = "Fix overlapping display times", + FixShortDisplayTimes = "Fix short display times", + FixLongDisplayTimes = "Fix long display times", + FixInvalidItalicTags = "Fix invalid italic tags", + RemoveUnneededSpaces = "Remove unneeded spaces", + RemoveUnneededPeriods = "Remove unneeded periods", + FixMissingSpaces = "Fix missing spaces", + BreakLongLines = "Break long lines", + RemoveLineBreaks = "Remove line breaks in short texts with only one sentence", + RemoveLineBreaksAll = "Remove line breaks in short texts (all except dialogs)", + FixUppercaseIInsindeLowercaseWords = "Fix uppercase 'i' inside lowercase words (OCR error)", + FixDoubleApostrophes = "Fix double apostrophe characters ('') to a single quote (\")", + AddPeriods = "Add period after lines where next line start with uppercase letter", + StartWithUppercaseLetterAfterParagraph = "Start with uppercase letter after paragraph", + StartWithUppercaseLetterAfterPeriodInsideParagraph = "Start with uppercase letter after period inside paragraph", + StartWithUppercaseLetterAfterColon = "Start with uppercase letter after colon/semicolon", + CommonOcrErrorsFixed = "Common OCR errors fixed (OcrReplaceList file used): {0}", + RemoveSpaceBetweenNumber = "Remove space between numbers", + FixDialogsOnOneLine = "Fix dialogs on one line", + RemoveSpaceBetweenNumbersFixed = "Remove space between numbers fixed: {0}", + FixLowercaseIToUppercaseI = "Fix alone lowercase 'i' to 'I' (English)", + FixTurkishAnsi = "Fix Turkish ANSI (Icelandic) letters to Unicode", + FixDanishLetterI = "Fix Danish letter 'i'", + FixSpanishInvertedQuestionAndExclamationMarks = "Fix Spanish inverted question and exclamation marks", + AddMissingQuote = "Add missing quote (\")", + AddMissingQuotes = "Add missing quotes (\")", + FixHyphens = "Fix (remove dash) lines beginning with dash (-)", + FixHyphensAdd = "Fix (add dash) line pairs with only one dash (-)", + FixHyphen = "Fix line beginning with dash (-)", + XHyphensFixed = "Dashes removed: {0}", + AddMissingQuotesExample = "\"How are you? -> \"How are you?\"", + XMissingQuotesAdded = "Missing quotes added: {0}", + Fix3PlusLines = "Fix subtitles with more than two lines", + Fix3PlusLine = "Fix subtitle with more than two lines", + X3PlusLinesFixed = "Subtitles with more than two lines fixed: {0}", + Analysing = "Analyzing...", + NothingToFix = "Nothing to fix :)", + FixesFoundX = "Fixes found: {0}", + XFixesApplied = "Fixes applied: {0}", + NothingToFixBut = "Nothing to fix but a few things could be improved - see log for details", + FixLowercaseIToUppercaseICheckedButCurrentLanguageIsNotEnglish = "\"Fix alone lowercase 'i' to 'I' (English)\" is checked but currently loaded subtitle seems not be English.", + Continue = "Continue", + ContinueAnyway = "Continue anyway?", + UncheckedFixLowercaseIToUppercaseI = "Unchecked \"Fix alone lowercase 'i' to 'I' (English)\"", + XIsChangedToUppercase = "{0} i's changed to uppercase", + FixFirstLetterToUppercaseAfterParagraph = "Fix first letter to uppercase after paragraph", + MergeShortLine = "Merge short line (single sentence)", + MergeShortLineAll = "Merge short line (all except dialogs)", + XLineBreaksAdded = "{0} line breaks added", + BreakLongLine = "Break long line", + FixLongDisplayTime = "Fix long display time", + FixInvalidItalicTag = "Fix invalid italic tag", + FixShortDisplayTime = "Fix short display time", + FixOverlappingDisplayTime = "Fix overlapping display time", + FixInvalidItalicTagsExample = "What do I care. -> What do I care.", + RemoveUnneededSpacesExample = "Hey you , there. -> Hey you, there.", + RemoveUnneededPeriodsExample = "Hey you!. -> Hey you!", + FixMissingSpacesExample = "Hey.You. -> Hey. You.", + FixUppercaseIInsindeLowercaseWordsExample = "The earth is fIat. -> The earth is flat.", + FixLowercaseIToUppercaseIExample = "What do i care. -> What do I care.", + StartTimeLaterThanEndTime = "Text number {0}: Start time is later than end time: {4}{1} -> {2} {3}", + UnableToFixStartTimeLaterThanEndTime = "Unable to fix text number {0}: Start time is later end end time: {1}", + XFixedToYZ = "{0} fixed to: {1}{2}", + UnableToFixTextXY = "Unable to fix text number {0}: {1}", + XOverlappingTimestampsFixed = "{0} overlapping timestamps fixed", + XDisplayTimesProlonged = "{0} display times prolonged", + XInvalidHtmlTagsFixed = "{0} invalid HTML tags fixed", + XDisplayTimesShortned = "{0} display times shortened", + XLinesUnbreaked = "{0} lines unbreaked", + UnneededSpace = "Unneeded space", + XUnneededSpacesRemoved = "{0} unneeded spaces removed", + UnneededPeriod = "Unneeded period", + XUnneededPeriodsRemoved = "{0} unneeded periods removed", + FixMissingSpace = "Fix missing space", + XMissingSpacesAdded = "{0} missing spaces added", + FixUppercaseIInsideLowercaseWord = "Fix uppercase 'i' inside lowercase word", + XPeriodsAdded = "{0} periods added.", + FixMissingPeriodAtEndOfLine = "Add missing period at end of line", + XDoubleApostrophesFixed = "{0} double apostrophes fixed.", + XUppercaseIsFoundInsideLowercaseWords = "{0} uppercase 'i's found inside lowercase words", + ApplyFixes = "Apply selected fixes", + RefreshFixes = "Refresh available fixes", + FixDoubleDash = "Fix '--' -> '...'", + FixDoubleGreaterThan = "Remove '>>'", + FixEllipsesStart = "Remove leading '...'", + FixMissingOpenBracket = "Fix missing [ in line", + FixMusicNotation = "Replace music symbols (e.g. âTª) with preferred symbol", + FixDoubleDashExample = "'Whoa-- um yeah!' -> 'Whoa... um yeah!'", + FixDoubleGreaterThanExample = "'>> Robert: Sup dude!' -> 'Robert: Sup dude!'", + FixEllipsesStartExample = "'... and then we' -> 'and then we'", + FixMissingOpenBracketExample = "'clanks] Look out!' -> '[clanks] Look out!'", + FixMusicNotationExample = "'âTª sweet dreams are' -> '♫ sweet dreams are'", + XFixDoubleDash = "{0} fixed '--'", + XFixDoubleGreaterThan = "{0} removed '>>'", + XFixEllipsesStart = "{0} remove starting '...'", + XFixMissingOpenBracket = "{0} fixed missing [ in line", + XFixMusicNotation = "{0} fix music notation in line", + AutoBreak = "Auto &br", + Unbreak = "&Unbreak", + FixCommonOcrErrors = "Fix common OCR errors (using OCR replace list)", + NumberOfImportantLogMessages = "{0} important log messages!", + FixedOkXY = "Fixed and OK - '{0}': {1}", + FixOcrErrorExample = "D0n't -> Don't", + FixSpaceBetweenNumbersExample = "1 100 -> 1100", + FixDialogsOneLineExample = "Hi John! - Hi Ida! -> Hi John!
- Hi Ida!", + }; + + GetDictionaries = new LanguageStructure.GetDictionaries + { + Title = "Need dictionaries?", + DescriptionLine1 = "Subtitle Edit's spell check is based on the NHunspell engine which", + DescriptionLine2 = "uses the spell checking dictionaries from Open Office.", + GetDictionariesHere = "Get dictionaries here:", + OpenOpenOfficeWiki = "Open Office Wiki Dictionaries list", + GetAllDictionaries = "Get all dictionaries", + OpenDictionariesFolder = "Open 'Dictionaries' folder", + Download = "Download", + ChooseLanguageAndClickDownload = "Choose your language and click download", + XDownloaded = "{0} has been downloaded and installed", + }; + + GetTesseractDictionaries = new LanguageStructure.GetTesseractDictionaries + { + Title = "Need dictionaries?", + DescriptionLine1 = "Get Tesseract OCR dictionaries from the web", + DownloadFailed = "Download failed!", + GetDictionariesHere = "Get dictionaries here:", + OpenDictionariesFolder = "Open 'Dictionaries' folder", + Download = "Download", + ChooseLanguageAndClickDownload = "Choose your language and click download", + XDownloaded = "{0} has been downloaded and installed", + }; + + GoogleTranslate = new LanguageStructure.GoogleTranslate + { + Title = "Google translate", + From = "From:", + To = "To:", + Translate = "Translate", + PleaseWait = "Please wait... this may take a while", + PoweredByGoogleTranslate = "Powered by Google translate", + PoweredByMicrosoftTranslate = "Powered by Microsoft translate", + }; + + GoogleOrMicrosoftTranslate = new LanguageStructure.GoogleOrMicrosoftTranslate + { + Title = "Google vs Microsoft translate", + From = "From:", + To = "To:", + Translate = "Translate", + SourceText = "Source text", + GoogleTranslate = "Google translate", + MicrosoftTranslate = "Microsoft translate", + }; + + GoToLine = new LanguageStructure.GoToLine + { + Title = "Go to subtitle number", + XIsNotAValidNumber = "{0} is not a valid number", + }; + + ImportImages = new LanguageStructure.ImportImages + { + Title = "Import images", + Input = "Input", + InputDescription = "Choose input files (browse or drag-n-drop)", + ImageFiles = "Image files", + }; + + ImportSceneChanges = new LanguageStructure.ImportSceneChanges + { + Title = "Import scene changes", + OpenTextFile = "Open text file...", + ImportOptions = "Import options", + TextFiles = "Text files", + TimeCodes = "Time codes", + Frames = "Frames", + Seconds = "Seconds", + Milliseconds = "Milliseconds", + }; + + ImportText = new LanguageStructure.ImportText + { + Title = "Import plain text", + OneSubtitleIsOneFile = "Multiple files - one file is one subtitle", + OpenTextFile = "Open text file...", + OpenTextFiles = "Open text files...", + ImportOptions = "Import options", + Splitting = "Splitting", + AutoSplitText = "Auto split text", + OneLineIsOneSubtitle = "One line is one subtitle", + LineBreak = "Line break", + SplitAtBlankLines = "Split at blank lines", + MergeShortLines = "Merge short lines with continuation", + RemoveEmptyLines = "Remove empty lines", + RemoveLinesWithoutLetters = "Remove lines without letters", + GenerateTimeCodes = "Generate time codes", + GapBetweenSubtitles = "Gap between subtitles (milliseconds)", + Auto = "Auto", + Fixed = "Fixed", + Refresh = "&Refresh", + TextFiles = "Text files", + PreviewLinesModifiedX = "Preview - subtitles modified: {0}", + TimeCodes = "Time codes", + }; + + Interjections = new LanguageStructure.Interjections + { + Title = "Interjections", + }; + + JoinSubtitles = new LanguageStructure.JoinSubtitles + { + Title = "Join subtitles", + Information = "Add subtitles to join (drop also supported)", + NumberOfLines = "#Lines", + StartTime = "Start time", + EndTime = "End time", + FileName = "File name", + Join = "Join", + TotalNumberOfLinesX = "Total number of lines: {0:#,###,###}", + Note = "Note: Files must already have correct time codes", + }; + + Main = new LanguageStructure.Main + { + SaveChangesToUntitled = "Save changes to untitled?", + SaveChangesToX = "Save changes to {0}?", + SaveChangesToUntitledOriginal = "Save changes to untitled original?", + SaveChangesToOriginalX = "Save changes to original {0}?", + SaveSubtitleAs = "Save subtitle as...", + SaveOriginalSubtitleAs = "Save original subtitle as...", + NoSubtitleLoaded = "No subtitle loaded", + VisualSyncSelectedLines = "Visual sync - selected lines", + VisualSyncTitle = "Visual sync", + BeforeVisualSync = "Before visual sync", + VisualSyncPerformedOnSelectedLines = "Visual sync performed on selected lines", + VisualSyncPerformed = "Visual sync performed", + ImportThisVobSubSubtitle = "Import this VobSub subtitle?", + FileXIsLargerThan10MB = "File is larger than 10 MB: {0}", + ContinueAnyway = "Continue anyway?", + BeforeLoadOf = "Before load of {0}", + LoadedSubtitleX = "Loaded subtitle {0}", + LoadedEmptyOrShort = "Loaded empty or very short subtitle {0}", + FileIsEmptyOrShort = "File is empty or very short!", + FileNotFound = "File not found: {0}", + SavedSubtitleX = "Saved subtitle {0}", + SavedOriginalSubtitleX = "Saved original subtitle {0}", + FileOnDiskModified = "File on disk modified", + OverwriteModifiedFile = "Overwrite the file {0} modified at {1} {2}{3} with current file loaded from disk at {4} {5}?", + FileXIsReadOnly = "Cannot save {0}" + Environment.NewLine + Environment.NewLine + "File is read-only!", + UnableToSaveSubtitleX = "Unable to save subtitle file {0}" + Environment.NewLine + "Subtitle seems to be empty - try to re-save if you're working on a valid subtitle!", + BeforeNew = "Before new", + New = "New", + BeforeConvertingToX = "Before converting to {0}", + ConvertedToX = "Converted to {0}", + BeforeShowEarlier = "Before show earlier", + BeforeShowLater = "Before show later", + LineNumberX = "Line number: {0:#,##0.##}", + OpenVideoFile = "Open video file...", + NewFrameRateUsedToCalculateTimeCodes = "New frame rate ({0}) was used for calculating start/end time codes", + NewFrameRateUsedToCalculateFrameNumbers = "New frame rate ({0}) was used for calculating start/end frame numbers", + FindContinueTitle = "Continue Find?", + FindContinue = "The search item was not found." + Environment.NewLine + + "Would you like to start from the top of the document and search one more time?", + ReplaceContinueTitle = "Continue 'Replace'?", + ReplaceContinueNotFound = "The search item was not found." + Environment.NewLine + + "Would you like to start from the top of the document and continue search and replace?", + + ReplaceXContinue = "The search item was replaced {0} time(s)." + Environment.NewLine + + "Would you like to start from the top of the document and continue search and replace?", + + SearchingForXFromLineY = "Searching for '{0}' from line number {1}...", + XFoundAtLineNumberY = "'{0}' found at line number {1}", + XNotFound = "'{0}' not found", + BeforeReplace = "Before replace: {0}", + MatchFoundX = "Match found: {0}", + NoMatchFoundX = "No match found: {0}", + FoundNothingToReplace = "Found nothing to replace", + ReplaceCountX = "Replace count: {0}", + NoXFoundAtLineY = "Match found at line {0}: {1}", + OneReplacementMade = "One replacement made.", + BeforeChangesMadeInSourceView = "Before changes made in source view", + UnableToParseSourceView = "Unable to parse source view text!", + GoToLineNumberX = "Go to line number {0}", + CreateAdjustChangesApplied = "Create/adjust lines changes applied", + SelectedLines = "selected lines", + BeforeDisplayTimeAdjustment = "Before display time adjustment", + DisplayTimesAdjustedX = "Display times adjusted: {0}", + DisplayTimeAdjustedX = "Display time adjusted: {0}", + StarTimeAdjustedX = "Start time adjusted: {0}", + BeforeCommonErrorFixes = "Before common error fixes", + CommonErrorsFixedInSelectedLines = "Common errors fixed in selected lines", + CommonErrorsFixed = "Common errors fixed", + BeforeRenumbering = "Before renumbering", + RenumberedStartingFromX = "Renumbered starting from: {0}", + BeforeRemovalOfTextingForHearingImpaired = "Before removal of texting for hearing impaired", + TextingForHearingImpairedRemovedOneLine = "Texting for hearing impaired removed: One line", + TextingForHearingImpairedRemovedXLines = "Texting for hearing impaired removed: {0} lines", + SubtitleSplitted = "Subtitle was split", + SubtitleAppendPrompt = "This will append an existing subtitle to the currently loaded subtitle which should" + Environment.NewLine + + "already be in sync with video file." + Environment.NewLine + + Environment.NewLine + + "Continue?", + SubtitleAppendPromptTitle = "Append subtitle", + OpenSubtitleToAppend = "Open subtitle to append...", + AppendViaVisualSyncTitle = "Visual sync - append second part of subtitle", + AppendSynchronizedSubtitlePrompt = "Append this synchronized subtitle?", + BeforeAppend = "Before append", + SubtitleAppendedX = "Subtitle appended: {0}", + SubtitleNotAppended = "Subtitle NOT appended!", + GoogleTranslate = "Google translate", + MicrosoftTranslate = "Microsoft translate", + BeforeGoogleTranslation = "Before Google translation", + SelectedLinesTranslated = "Selected lines translated", + SubtitleTranslated = "Subtitle translated", + TranslateSwedishToDanish = "Translate currently loaded Swedish subtitle to Danish", + TranslateSwedishToDanishWarning = "Translate currently loaded SWEDISH (are you sure it's Swedish?) subtitle to Danish?", + TranslatingViaNikseDkMt = "Translating via www.nikse.dk/mt...", + BeforeSwedishToDanishTranslation = "Before Swedish to Danish translation", + TranslationFromSwedishToDanishComplete = "Translation from Swedish to Danish complete", + TranslationFromSwedishToDanishFailed = "Translation from Swedish to Danish failed", + BeforeUndo = "Before undo", + UndoPerformed = "Undo performed", + RedoPerformed = "Redo performed", + NothingToUndo = "Nothing to undo", + InvalidLanguageNameX = "Invalid language name: {0}", + UnableToChangeLanguage = "Unable to change language!", + DoNotDisplayMessageAgain = "Don't display this message again", + NumberOfCorrectedWords = "Number of corrected words: {0}", + NumberOfSkippedWords = "Number of skipped words: {0}", + NumberOfCorrectWords = "Number of correct words: {0}", + NumberOfWordsAddedToDictionary = "Number of words added to dictionary: {0}", + NumberOfNameHits = "Number of name hits: {0}", + SpellCheck = "Spell check", + BeforeSpellCheck = "Before spell check", + SpellCheckChangedXToY = "Spell check: Changed '{0}' to '{1}'", + BeforeAddingTagX = "Before adding <{0}> tag", + TagXAdded = "<{0}> tags added", + LineXOfY = "line {0} of {1}", + XLinesSavedAsY = "{0} lines saved as {1}", + XLinesDeleted = "{0} lines deleted", + BeforeDeletingXLines = "Before deleting {0} lines", + DeleteXLinesPrompt = "Delete {0} lines?", + OneLineDeleted = "Line deleted", + BeforeDeletingOneLine = "Before deleting one line", + DeleteOneLinePrompt = "Delete one line?", + BeforeInsertLine = "Before insert line", + BeforeLineUpdatedInListView = "Before line updated in listview", + LineInserted = "Line inserted", + BeforeSettingFontToNormal = "Before setting font to normal", + BeforeSplitLine = "Before split line", + LineSplitted = "Line was split", + BeforeMergeLines = "Before merge lines", + LinesMerged = "Lines merged", + BeforeSettingColor = "Before setting color", + BeforeSettingFontName = "Before setting font name", + BeforeTypeWriterEffect = "Before typewriter effect", + BeforeKaraokeEffect = "Before karaoke effect", + BeforeImportingDvdSubtitle = "Before importing subtitle from DVD", + OpenMatroskaFile = "Open Matroska file...", + MatroskaFiles = "Matroska files", + NoSubtitlesFound = "No subtitles found", + NotAValidMatroskaFileX = "This is not a valid Matroska file: {0}", + BlurayNotSubtitlesFound = "Blu-ray sup file does not contain any subtitles or contains errors - try demuxing again.", + ParsingMatroskaFile = "Parsing Matroska file. Please wait...", + ParsingTransportStreamFile = "Parsing Transport Stream file. Please wait...", + BeforeImportFromMatroskaFile = "Before import subtitle from Matroska file", + SubtitleImportedFromMatroskaFile = "Subtitle imported from Matroska file", + DropFileXNotAccepted = "Drop file '{0}' not accepted - file is too large", + DropOnlyOneFile = "You can only drop one file", + BeforeCreateAdjustLines = "Before create/adjust lines", + OpenAnsiSubtitle = "Open subtitle...", + BeforeChangeCasing = "Before change casing", + CasingCompleteMessage = "Number of lines with casing changed: {0}/{1}, changed casing for names: {2}", + CasingCompleteMessageNoNames = "Number of lines with casing changed: {0}/{1}", + CasingCompleteMessageOnlyNames = "Number of lines with names casing changed: {0}/{1}", + BeforeChangeFrameRate = "Before change frame rate", + BeforeAdjustSpeedInPercent = "Before adjust speed in percent", + FrameRateChangedFromXToY = "Frame rate changed from {0} to {1}", + IdxFileNotFoundWarning = "{0} not found! Import VobSub file anyway?", + InvalidVobSubHeader = "Header not valid VobSub file: {0}", + OpenVobSubFile = "Open VobSub (sub/idx) subtitle...", + VobSubFiles = "VobSub subtitle files", + OpenBluRaySupFile = "Open Blu-ray .sup file...", + BluRaySupFiles = "Blu-ray .sup files", + OpenXSubFiles = "Open XSub file...", + XSubFiles = "XSub files", + BeforeImportingVobSubFile = "Before importing VobSub subtitle", + BeforeImportingBluRaySupFile = "Before importing Blu-ray sup file", + BeforeImportingBdnXml = "Before importing BDN xml file", + BeforeShowSelectedLinesEarlierLater = "Before show selected lines earlier/later", + ShowAllLinesXSecondsLinesEarlier = "Show all lines {0:0.0##} seconds earlier", + ShowAllLinesXSecondsLinesLater = "Show all lines {0:0.0##} seconds later", + ShowSelectedLinesXSecondsLinesEarlier = "Show selected lines {0:0.0##} seconds earlier", + ShowSelectedLinesXSecondsLinesLater = "Show selected lines {0:0.0##} seconds later", + ShowSelectionAndForwardXSecondsLinesEarlier = "Show selection and forward {0:0.0##} seconds earlier", + ShowSelectionAndForwardXSecondsLinesLater = "Show selection and forward {0:0.0##} seconds later", + ShowSelectedLinesEarlierLaterPerformed = "Show earlier/later performed on selected lines", + DoubleWordsViaRegEx = "Double words via regex {0}", + BeforeSortX = "Before sort: {0}", + SortedByX = "Sorted by: {0}", + BeforeAutoBalanceSelectedLines = "Before auto balance selected lines", + NumberOfLinesAutoBalancedX = "Number of auto balanced lines: {0}", + BeforeRemoveLineBreaksInSelectedLines = "Before remove line-breaks from selected lines", + NumberOfWithRemovedLineBreakX = "Number of lines with removed line-break: {0}", + BeforeMultipleReplace = "Before multiple replace", + NumberOfLinesReplacedX = "Number of lines with text replaced: {0}", + NameXAddedToNamesEtcList = "The name '{0}' was added to names/etc list", + NameXNotAddedToNamesEtcList = "The name '{0}' was NOT added to names/etc list", + WordXAddedToUserDic = "The word '{0}' was added to the user dictionary", + WordXNotAddedToUserDic = "The word '{0}' was NOT added to the user dictionary", + OcrReplacePairXAdded = "The OCR replace list pair '{0} -> {1}' was added to the OCR replace list", + OcrReplacePairXNotAdded = "The OCR replace list pair '{0} -> {1}' was NOT added to the OCR replace list", + XLinesSelected = "{0} lines selected", + UnicodeMusicSymbolsAnsiWarning = "Subtitle contains unicode characters. Saving using ANSI file encoding will lose these. Continue with saving?", + UnicodeCharactersAnsiWarning = "Subtitle contains unicode characters. Saving using ANSI file encoding will lose these. Continue with saving?", + NegativeTimeWarning = "Subtitle contains negative time codes. Continue with saving?", + BeforeMergeShortLines = "Before merge short lines", + BeforeSplitLongLines = "Before split long lines", + MergedShortLinesX = "Number of lines merged: {0}", + BeforeDurationsBridgeGap = "Before bridge small gaps", + BeforeSetMinimumDisplayTimeBetweenParagraphs = "Before set minimum display time between subtitles", + XMinimumDisplayTimeBetweenParagraphsChanged = "Number of lines with minimum display time between subtitles changed: {0}", + BeforeImportText = "Before import plain text", + TextImported = "Text imported", + BeforePointSynchronization = "Before point synchronization", + PointSynchronizationDone = "Point synchronization done", + BeforeTimeCodeImport = "Before import of time codes", + TimeCodeImportedFromXY = "Time codes imported from {0}: {1}", + BeforeInsertSubtitleAtVideoPosition = "Before insert subtitle at video position", + BeforeSetStartTimeAndOffsetTheRest = "Before set start time and off-set the rest", + BeforeSetEndTimeAndOffsetTheRest = "Before set end time and off-set the rest", + BeforeSetEndAndVideoPosition = "Before set end time at video position and auto calculate start", + ContinueWithCurrentSpellCheck = "Continue with current spell check?", + CharactersPerSecond = "Chars/sec: {0:0.00}", + GetFrameRateFromVideoFile = "Get frame rate from video file", + NetworkMessage = "New message: {0} ({1}): {2}", + NetworkUpdate = "Line updated: {0} ({1}): Index={2}, Text={3}", + NetworkInsert = "Line inserted: {0} ({1}): Index={2}, Text={3}", + NetworkDelete = "Line deleted: {0} ({1}): Index={2}", + NetworkNewUser = "New user: {0} ({1})", + NetworkByeUser = "Bye {0} ({1})", + NetworkUnableToConnectToServer = "Unable to connect to server: {0}", + NetworkMode = "Networking mode", + UserAndAction = "User/action", + XStartedSessionYAtZ = "{0}: Started session {1} at {2}", + SpellChekingViaWordXLineYOfX = "Spell checking using Word {0} - line {1} / {2}", + UnableToStartWord = "Unable to start Microsoft Word", + SpellCheckAbortedXCorrections = "Spell check aborted. {0} lines were modified.", + SpellCheckCompletedXCorrections = "Spell check completed. {0} lines were modified.", + OpenOtherSubtitle = "Open other subtitle", + BeforeToggleDialogDashes = "Before toggle of dialog dashes", + TextFiles = "Text files", + ExportPlainTextAs = "Export plain text as", + SubtitleExported = "Subtitle exported", + LineNumberXErrorReadingFromSourceLineY = "Line {0} - error reading: {1}", + LineNumberXErrorReadingTimeCodeFromSourceLineY = "Line {0} - error reading time code: {1}", + LineNumberXExpectedNumberFromSourceLineY = "Line {0} - expected subtitle number: {1}", + BeforeGuessingTimeCodes = "Before guessing time codes", + BeforeAutoDuration = "Before auto-duration for selected lines", + BeforeColumnPaste = "Before column paste", + BeforeColumnDelete = "Before column delete", + BeforeColumnImportText = "Before column import text", + BeforeColumnShiftCellsDown = "Before column shift cells down", + ErrorLoadingPluginXErrorY = "Error loading plugin: {0}: {1}", + BeforeRunningPluginXVersionY = "Before running plugin: {0}: {1}", + UnableToReadPluginResult = "Unable to read subtitle result from plugin!", + UnableToCreateBackupDirectory = "Unable to create backup directory {0}: {1}", + BeforeDisplaySubtitleJoin = "Before join of subtitles", + SubtitlesJoined = "Subtitles joined", + StatusLog = "Status log", + XSceneChangesImported = "{0} scene changes imported", + PluginXExecuted = "Plugin '{0}' executed.", + NotAValidXSubFile = "Not a valid XSub file!", + BeforeMergeLinesWithSameText = "Before merging lines with same text", + ImportTimeCodesDifferentNumberOfLinesWarning = "Subtitle with time codes has a different number of lines ({0}) than current subtitle ({1}) - continue anyway?", + ParsingTransportStream = "Parsing transport stream - please wait...", + XPercentCompleted = "{0}% completed", + ErrorLoadIdx = "Cannot read/edit .idx files. Idx files are a part of an idx/sub file pair (also called VobSub), and Subtitle Edit can open the .sub file.", + ErrorLoadRar = "This file seems to be a compressed .rar file. Subtitle Edit cannot open compressed files.", + ErrorLoadZip = "This file seems to be a compressed .zip file. Subtitle Edit cannot open compressed files.", + ErrorLoadPng = "This file seems to be a PNG image file. Subtitle Edit cannot open PNG files.", + ErrorLoadJpg = "This file seems to be a JPG image file. Subtitle Edit cannot open JPG files.", + ErrorLoadSrr = "This file seems to be a ReScene .srr file - not a subtitle file.", + ErrorLoadTorrent = "This file seems to be a BitTorrent file - not a subtitle file.", + ErrorLoadBinaryZeroes = "Sorry, this file contains only binary zeroes!\r\n\r\nIf you have edited this file with Subtitle Edit you might be alble to find a backup via the menu item File -> Restore auto-backup...", + ErrorDirectoryDropNotAllowed = "Directory drop are not supported here.", + NoSupportEncryptedVobSub = "Encrypted VobSub content is not supported.", + NoSupportHereBluRaySup = "Blu-ray sup files are not supported here.", + NoSupportHereDvdSup = "DVD sup files are not supported here.", + NoSupportHereVobSub = "VobSub files are not supported here.", + NoSupportHereDivx = "Divx files are not supported here.", + + Menu = new LanguageStructure.Main.MainMenu + { + File = new LanguageStructure.Main.MainMenu.FileMenu + { + Title = "&File", + New = "&New", + Open = "&Open", + OpenKeepVideo = "Open (keep video)", + Reopen = "&Reopen", + Save = "&Save", + SaveAs = "Save &as...", + RestoreAutoBackup = "Restore auto-backup...", + AdvancedSubStationAlphaProperties = "Advanced Sub Station Alpha properties...", + SubStationAlphaProperties = "Sub Station Alpha properties...", + EbuProperties = "EBU STL properties...", + PacProperties = "PAC properties...", + OpenOriginal = "Open original subtitle (translator mode)...", + SaveOriginal = "Save original subtitle", + CloseOriginal = "Close original subtitle", + OpenContainingFolder = "Open containing folder", + Compare = "&Compare...", + Statistics = "Statisti&cs...", + Plugins = "Plugins...", + ImportOcrFromDvd = "Import/OCR subtitle from VOB/IFO (DVD)...", + ImportOcrVobSubSubtitle = "Import/OCR VobSub (sub/idx) subtitle...", + ImportBluRaySupFile = "Import/OCR Blu-ray (.sup) subtitle file...", + ImportXSub = "Import/OCR XSub from divx/avi...", + ImportSubtitleFromMatroskaFile = "Import subtitle from Matroska (.mkv) file...", + ImportSubtitleWithManualChosenEncoding = "Import subtitle with manual chosen encoding...", + ImportText = "Import plain text...", + ImportImages = "Import images...", + ImportTimecodes = "Import time codes...", + Export = "Export", + ExportBdnXml = "BDN xml/png...", + ExportBluRaySup = "Blu-ray sup...", + ExportVobSub = "VobSub (sub/idx)...", + ExportCavena890 = "Cavena 890...", + ExportEbu = "EBU STL...", + ExportPac = "PAC (Screen Electronics)...", + ExportPlainText = "Plain text...", + ExportAvidStl = "Avid STL...", + ExportAdobeEncoreFabImageScript = "Adobe Encore FAB image script...", + ExportKoreanAtsFilePair = "Korean ATS file pair...", + ExportDvdStudioProStl = "DVD Studio Pro STL...", + ExportCapMakerPlus = "CapMaker Plus...", + ExportCaptionsInc = "Captions Inc...", + ExportCheetahCap = "Cheetah CAP...", + ExportUltech130 = "Ultech caption...", + ExportCustomTextFormat = "Export custom text format...", + Exit = "E&xit" + }, + + Edit = new LanguageStructure.Main.MainMenu.EditMenu + { + Title = "Edit", + Undo = "Undo", + Redo = "Redo", + ShowUndoHistory = "Show history (for undo)", + InsertUnicodeSymbol = "Insert Unicode symbol", + InsertUnicodeControlCharacters = "Insert Unicode control characters", + InsertUnicodeControlCharactersLRM = "Left-to-right mark (LRM)", + InsertUnicodeControlCharactersRLM = "Right-to-left mark (RLM)", + InsertUnicodeControlCharactersLRE = "Start of left-to-right embedding (LRE)", + InsertUnicodeControlCharactersRLE = "Start of right-to-left embedding (RLE)", + InsertUnicodeControlCharactersLRO = "Start of left-to-right override (LRO)", + InsertUnicodeControlCharactersRLO = "Start of right-to-left override (RLO)", + Find = "&Find", + FindNext = "Find &next", + Replace = "&Replace", + MultipleReplace = "&Multiple replace...", + GoToSubtitleNumber = "&Go to subtitle number...", + RightToLeftMode = "Right-to-left mode", + FixTrlViaUnicodeControlCharacters = "Fix RTL via Unicode control characters (for selected lines)", + ReverseRightToLeftStartEnd = "Reverse RTL start/end (for selected lines)", + ShowOriginalTextInAudioAndVideoPreview = "Show original text in audio/video previews", + ModifySelection = "Modify selection...", + InverseSelection = "Invert selection", + }, + + Tools = new LanguageStructure.Main.MainMenu.ToolsMenu + { + Title = "Tools", + AdjustDisplayDuration = "&Adjust durations...", + ApplyDurationLimits = "Apply duration limits...", + DurationsBridgeGap = "Bridge gap in durations...", + FixCommonErrors = "&Fix common errors...", + StartNumberingFrom = "Renumber...", + RemoveTextForHearingImpaired = "Remove text for hearing impaired...", + ChangeCasing = "Change casing...", + ChangeFrameRate = "Change frame rate...", + ChangeSpeedInPercent = "Changed speed (percent)...", + MergeShortLines = "Merge short lines...", + MergeDuplicateText = "Merge lines with same text...", + MergeSameTimeCodes = "Merge lines with same time codes...", + SplitLongLines = "Split long lines...", + MinimumDisplayTimeBetweenParagraphs = "Minimum display time between subtitles...", + SortBy = "Sort by", + Number = "Number", + StartTime = "Start time", + EndTime = "End time", + Duration = "Duration", + TextAlphabetically = "Text - alphabetically", + TextSingleLineMaximumLength = "Text - single line max. length", + TextTotalLength = "Text - total length", + TextNumberOfLines = "Text - number of lines", + TextNumberOfCharactersPerSeconds = "Text - number of chars/sec", + WordsPerMinute = "Text - words per minute (wpm)", + Style = "Style", + Ascending = "Ascending", + Descending = "Descending", + MakeNewEmptyTranslationFromCurrentSubtitle = "Make new empty translation from current subtitle", + BatchConvert = "Batch convert...", + GenerateTimeAsText = "Generate time as text...", + MeasurementConverter = "Measurement converter...", + SplitSubtitle = "Split subtitle...", + AppendSubtitle = "Append subtitle...", + JoinSubtitles = "Join subtitles...", + }, + + Video = new LanguageStructure.Main.MainMenu.VideoMenu + { + Title = "Video", + OpenVideo = "Open video file...", + OpenDvd = "Open DVD...", + ChooseAudioTrack = "Choose audio track", + CloseVideo = "Close video file", + ImportSceneChanges = "Import scene changes...", + RemoveSceneChanges = "Remove scene changes", + WaveformBatchGenerate = "Batch generate waveforms...", + ShowHideVideo = "Show/hide video", + ShowHideWaveform = "Show/hide waveform", + ShowHideWaveformAndSpectrogram = "Show/hide waveform and spectrogram", + UnDockVideoControls = "Un-dock video controls", + ReDockVideoControls = "Re-dock video controls", + }, + + SpellCheck = new LanguageStructure.Main.MainMenu.SpellCheckMenu + { + Title = "Spell check", + FindDoubleWords = "Find double words", + FindDoubleLines = "Find double lines", + SpellCheck = "&Spell check...", + SpellCheckFromCurrentLine = "Spell check from current line...", + GetDictionaries = "Get dictionaries...", + AddToNamesEtcList = "Add word to names/etc list", + }, + + Synchronization = new LanguageStructure.Main.MainMenu.SynchronizationkMenu + { + Title = "Synchronization", + AdjustAllTimes = "Adjust all times (show earlier/later)...", + VisualSync = "&Visual sync...", + PointSync = "Point sync...", + PointSyncViaOtherSubtitle = "Point sync via other subtitle...", + }, + + AutoTranslate = new LanguageStructure.Main.MainMenu.AutoTranslateMenu + { + Title = "Auto-translate", + TranslatePoweredByGoogle = "Translate (powered by Google)...", + TranslatePoweredByMicrosoft = "Translate (powered by Microsoft)...", + TranslateFromSwedishToDanish = "Translate from Swedish to Danish (powered by nikse.dk)...", + }, + + Options = new LanguageStructure.Main.MainMenu.OptionsMenu + { + Title = "Options", + Settings = "&Settings...", + ChooseLanguage = "&Choose language...", + }, + + Help = new LanguageStructure.Main.MainMenu.HelpMenu + { + CheckForUpdates = "Check for updates...", + Title = "Help", + Help = "&Help", + About = "&About" + }, + + Networking = new LanguageStructure.Main.MainMenu.NetworkingMenu + { + Title = "Networking", + StartNewSession = "Start new session", + JoinSession = "Join session", + ShowSessionInfoAndLog = "Show session info and log", + Chat = "Chat", + LeaveSession = "Leave session", + }, + + ToolBar = new LanguageStructure.Main.MainMenu.ToolBarMenu + { + New = "New", + Open = "Open", + Save = "Save", + SaveAs = "Save as", + Find = "Find", + Replace = "Replace", + FixCommonErrors = "Fix common errors", + VisualSync = "Visual sync", + SpellCheck = "Spell check", + Settings = "Settings", + Help = "Help", + ShowHideWaveform = "Show/hide waveform", + ShowHideVideo = "Show/hide video", + }, + + ContextMenu = new LanguageStructure.Main.MainMenu.ListViewContextMenu + { + AdvancedSubStationAlphaSetStyle = "Advanced Sub Station Alpha - set style", + SubStationAlphaSetStyle = "Sub Station Alpha - set style", + AdvancedSubStationAlphaStyles = "Advanced Sub Station Alpha styles...", + SubStationAlphaStyles = "Sub Station Alpha styles...", + TimedTextStyles = "Timed Text styles...", + TimedTextSetStyle = "Timed Text - set style", + TimedTextSetLanguage = "Timed Text - set language", + SamiSetStyle = "Sami - set class", + NuendoSetStyle = "Nuendo - set character", + Cut = "Cut", + Copy = "Copy", + Paste = "Paste", + Delete = "Delete", + SplitLineAtCursorPosition = "Split line at cursor position", + AutoDurationCurrentLine = "Auto duration (current line)", + SelectAll = "Select all", + InsertFirstLine = "Insert line", + InsertBefore = "Insert before", + InsertAfter = "Insert after", + InsertSubtitleAfter = "Insert subtitle after this line...", + CopyToClipboard = "Copy as text to clipboard", + Column = "Column", + ColumnDeleteText = "Delete text", + ColumnDeleteTextAndShiftCellsUp = "Delete text and shift cells up", + ColumnInsertEmptyTextAndShiftCellsDown = "Insert empty text and shift cells down", + ColumnInsertTextFromSubtitle = "Insert text from subtitle...", + ColumnImportTextAndShiftCellsDown = "Import text and shift cells down", + ColumnPasteFromClipboard = "Paste from clipboard...", + ColumnCopyOriginalTextToCurrent = "Copy text from original to current", + Split = "Split", + MergeSelectedLines = "Merge selected lines", + MergeSelectedLinesAsDialog = "Merge selected lines as dialog", + MergeWithLineBefore = "Merge with line before", + MergeWithLineAfter = "Merge with line after", + Normal = "Normal", + Underline = "Underline", + Color = "Color...", + FontName = "Font name...", + Alignment = "Alignment...", + AutoBalanceSelectedLines = "Auto balance selected lines...", + RemoveLineBreaksFromSelectedLines = "Remove line-breaks from selected lines...", + TypewriterEffect = "Typewriter effect...", + KaraokeEffect = "Karaoke effect...", + ShowSelectedLinesEarlierLater = "Show selected lines earlier/later...", + VisualSyncSelectedLines = "Visual sync selected lines...", + GoogleAndMicrosoftTranslateSelectedLine = "Google/Microsoft translate original line", + GoogleTranslateSelectedLines = "Google translate selected lines...", + AdjustDisplayDurationForSelectedLines = "Adjust durations for selected lines...", + FixCommonErrorsInSelectedLines = "Fix common errors in selected lines...", + ChangeCasingForSelectedLines = "Change casing for selected lines...", + SaveSelectedLines = "Save selected lines as...", + WebVTTSetNewVoice = "Set new voice...", + WebVTTRemoveVoices = "Remove voices", + } + }, + + Controls = new LanguageStructure.Main.MainControls + { + SubtitleFormat = "Format", + FileEncoding = "Encoding", + ListView = "List view", + SourceView = "Source view", + UndoChangesInEditPanel = "Undo changes in edit panel", + Previous = "< Prev", + Next = "Next >", + AutoBreak = "Auto &br", + Unbreak = "Unbreak" + }, + + VideoControls = new LanguageStructure.Main.MainVideoControls + { + Translate = "Translate", + Create = "Create", + Adjust = "Adjust", + SelectCurrentElementWhilePlaying = "Select current subtitle while playing", + + AutoRepeat = "Auto repeat", + AutoRepeatOn = "Auto repeat on", + AutoRepeatCount = "Repeat count (times)", + AutoContinue = "Auto continue", + AutoContinueOn = "Auto continue on", + DelayInSeconds = "Delay (seconds)", + Previous = "< Pre&vious", + Next = "&Next >", + PlayCurrent = "&Play current", + Stop = "&Stop", + Playing = "Playing...", + RepeatingLastTime = "Repeating... last time", + RepeatingXTimesLeft = "Repeating... {0} times left", + AutoContinueInOneSecond = "Auto continue in one second", + AutoContinueInXSeconds = "Auto continue in {0} seconds", + StillTypingAutoContinueStopped = "Still typing... auto continue stopped", + + InsertNewSubtitleAtVideoPosition = "&Insert new subtitle at video pos", + Auto = "Auto", + PlayFromJustBeforeText = "Play from just before &text", + Pause = "Pause", + GoToSubtitlePositionAndPause = "Go to sub position and pause", + SetStartTime = "Set &start time", + SetEndTimeAndGoToNext = "Set &end && go to next", + AdjustedViaEndTime = "Adjusted via end time {0}", + SetEndTime = "Set e&nd time", + SetstartTimeAndOffsetOfRest = "Set sta&rt and off-set the rest", + + GoogleIt = "Google it", + GoogleTranslate = "Google translate", + OriginalText = "Original text", + SearchTextOnline = "Search text online", + SecondsBackShort = "<<", + SecondsForwardShort = ">>", + VideoPosition = "Video position:", + TranslateTip = "Tip: Use to go to previous/next subtitle", + CreateTip = "Tip: Use keys", + AdjustTip = "Tip: Use to go to previous/next subtitle", + + BeforeChangingTimeInWaveformX = "Before changing time in waveform: {0}", + NewTextInsertAtX = "New text inserted at {0}", + + Center = "Center", + PlayRate = "Play rate (speed)", + Slow = "Slow", + Normal = "Normal", + Fast = "Fast", + VeryFast = "Very fast", + }, + }; + + MatroskaSubtitleChooser = new LanguageStructure.MatroskaSubtitleChooser + { + Title = "Choose subtitle from Matroska file", + PleaseChoose = "More than one subtitle found - please choose", + TrackXLanguageYTypeZ = "Track {0} - language: {1} - type: {2}", + }; + + MeasurementConverter = new LanguageStructure.MeasurementConverter + { + Title = "Measurement converter", + ConvertFrom = "Convert from", + ConvertTo = "Convert to", + CopyToClipboard = "Copy to clipboard", + Celsius = "Celsius", + Fahrenheit = "Fahrenheit", + Miles = "Miles", + Kilometers = "Kilometers", + Meters = "Meters", + Yards = "Yards", + Feet = "Feet", + Inches = "Inches", + Pounds = "Pounds", + Kilos = "Kilos", + }; + + MergeDoubleLines = new LanguageStructure.MergeDoubleLines + { + Title = "Merge lines with same text", + MaxMillisecondsBetweenLines = "Max. milliseconds between lines", + IncludeIncrementing = "Include incrementing lines", + }; + + MergedShortLines = new LanguageStructure.MergeShortLines + { + Title = "Merge short lines", + MaximumCharacters = "Maximum characters in one paragraph", + MaximumMillisecondsBetween = "Maximum milliseconds between lines", + NumberOfMergesX = "Number of merges: {0}", + MergedText = "Merged text", + OnlyMergeContinuationLines = "Only merge continuation lines", + }; + + MergeTextWithSameTimeCodes = new LanguageStructure.MergeTextWithSameTimeCodes + { + Title = "Merge lines with same time codes", + MaxDifferenceMilliseconds = "Max. milliseconds difference", + ReBreakLines = "Re-break lines", + NumberOfMergesX = "Number of merges: {0}", + MergedText = "Merged text", + }; + + ModifySelection = new LanguageStructure.ModifySelection + { + Title = "Modify selection", + Rule = "Rule", + CaseSensitive = "Case sensitive", + DoWithMatches = "What to do with matches", + MakeNewSelection = "Make new selection", + AddToCurrentSelection = "Add to current selection", + SubtractFromCurrentSelection = "Subtract from current selection", + IntersectWithCurrentSelection = "Intersect with current selection", + MatchingLinesX = "Matching lines: {0}", + Contains = "Contains", + StartsWith = "Starts with", + EndsWith = "Ends with", + NoContains = "Not contains", + RegEx = "Regular expression", + UnequalLines = "Odd-numbered lines", + EqualLines = "Even-numbered lines", + }; + + MultipleReplace = new LanguageStructure.MultipleReplace + { + Title = "Multiple replace", + FindWhat = "Find what", + ReplaceWith = "Replace with", + Normal = "Normal", + CaseSensitive = "Case sensitive", + RegularExpression = "Regular expression", + LinesFoundX = "Lines found: {0}", + Delete = "Delete", + Add = "&Add", + Update = "&Update", + Enabled = "Enabled", + SearchType = "Search type", + RemoveAll = "Remove all", + Import = "Import...", + Export = "Export...", + ImportRulesTitle = "Import replace rule(s) from...", + ExportRulesTitle = "Export replace rule(s) to...", + Rules = "Find and replace rules", + MoveToBottom = "Move to bottom", + MoveToTop = "Move to top" + }; + + NetworkChat = new LanguageStructure.NetworkChat + { + Title = "Chat", + Send = "Send", + }; + + NetworkJoin = new LanguageStructure.NetworkJoin + { + Title = "Join network session", + Information = @"Join existing session where multiple persons +can edit in same subtitle file (collaboration)", + Join = "Join", + }; + + NetworkLogAndInfo = new LanguageStructure.NetworkLogAndInfo + { + Title = "Network session info and log", + Log = "Log:" + }; + + NetworkStart = new LanguageStructure.NetworkStart + { + Title = "Start network session", + ConnectionTo = "Connecting to {0}...", + Information = @"Start new session where multiple persons +can edit in same subtitle file (collaboration)", + Start = "Start", + }; + + OpenVideoDvd = new LanguageStructure.OpenVideoDvd + { + Title = "Open DVD via VLC", + OpenDvdFrom = "Open DVD from...", + Disc = "Disc", + Folder = "Folder", + ChooseDrive = "Choose drive", + ChooseFolder = "Choose folder", + }; + + PluginsGet = new LanguageStructure.PluginsGet + { + Title = "Plugins", + InstalledPlugins = "Installed plugins", + GetPlugins = "Get plugins", + Description = "Description", + Version = "Version", + Date = "Date", + Type = "Type", + OpenPluginsFolder = "Open 'Plugins' folder", + GetPluginsInfo1 = "Subtitle Edit plugins must be downloaded to the 'Plugins' folder", + GetPluginsInfo2 = "Choose plugin and click 'Download'", + PluginXDownloaded = "Plugin {0} downloaded", + Download = "&Download", + Remove = "&Remove", + UpdateAllX = "Update all ({0})", + UnableToDownloadPluginListX = "Unable to download plug list: {0}", + NewVersionOfSubtitleEditRequired = "Newer version of Subtitle Edit required!", + UpdateAvailable = "[Update available!]", + UpdateAll = "Update all", + XPluginsUpdated = "{0} plugin(s) updated", + }; + + RegularExpressionContextMenu = new LanguageStructure.RegularExpressionContextMenu + { + WordBoundary = "Word boundary (\\b)", + NonWordBoundary = "Non word boundary (\\B)", + NewLine = "New line (\\r\\n)", + NewLineShort = "New line (\\n)", + AnyDigit = "Any digit (\\d)", + NonDigit = "Non digit (\\D)", + AnyCharacter = "Any character (.)", + NonSpaceCharacter = "Non space character (\\S)", + AnyWhitespace = "Any whitespace (\\s)", + ZeroOrMore = "Zero or more (*)", + OneOrMore = "One or more (+)", + InCharacterGroup = "In character group ([test])", + NotInCharacterGroup = "Not in character group ([^test])", + }; + + RemoveTextFromHearImpaired = new LanguageStructure.RemoveTextFromHearImpaired + { + Title = "Remove text for hearing impaired", + RemoveTextConditions = "Remove text conditions", + RemoveTextBetween = "Remove text between", + SquareBrackets = "'[' and ']'", + Brackets = "'{' and '}'", + QuestionMarks = "'?' and '?'", + Parentheses = "'(' and ')'", + And = "and", + RemoveTextBeforeColon = "Remove text before a colon (':')", + OnlyIfTextIsUppercase = "Only if text is UPPERCASE", + OnlyIfInSeparateLine = "Only if in separate line", + LinesFoundX = "Lines found: {0}", + RemoveTextIfContains = "Remove text if it contains:", + RemoveTextIfAllUppercase = "Remove line if UPPERCASE", + RemoveInterjections = "Remove interjections (shh, hmm, etc.)", + EditInterjections = "Edit...", + }; + + ReplaceDialog = new LanguageStructure.ReplaceDialog + { + Title = "Replace", + FindWhat = "Find what:", + Normal = "&Normal", + CaseSensitive = "&Case sensitive", + RegularExpression = "Regular e&xpression", + ReplaceWith = "Replace with", + Find = "&Find", + Replace = "&Replace", + ReplaceAll = "Replace &all", + }; + + RestoreAutoBackup = new LanguageStructure.RestoreAutoBackup + { + Title = "Restore auto backup", + Information = "Open auto-saved backup", + DateAndTime = "Date and time", + FileName = "File name", + Extension = "Extension", + NoBackedUpFilesFound = "No backup files found!", + }; + + SeekSilence = new LanguageStructure.SeekSilence + { + Title = "Seek silence", + SearchDirection = "Search direction", + Forward = "Forward", + Back = "Back", + LengthInSeconds = "Silence must be at at least (seconds)", + MaxVolume = "Volume must be below", + }; + + SetMinimumDisplayTimeBetweenParagraphs = new LanguageStructure.SetMinimumDisplayTimeBetweenParagraphs + { + Title = "Set minimum display time between subtitles", + PreviewLinesModifiedX = "Preview - subtitles modified: {0}", + MinimumMillisecondsBetweenParagraphs = "Minimum milliseconds between lines", + ShowOnlyModifiedLines = "Show only modified lines", + FrameInfo = "Frame rate info", + OneFrameXisYMilliseconds = "One frame at {0:0.00} fps is {1} milliseconds", + }; + + SetSyncPoint = new LanguageStructure.SetSyncPoint + { + Title = "Set Sync point for line {0}", + SyncPointTimeCode = "Sync point time code", + ThreeSecondsBack = "<< 3 s", + HalfASecondBack = "<< ½ s", + HalfASecondForward = "½ s >>", + ThreeSecondsForward = "3 s >>", + }; + + Settings = new LanguageStructure.Settings + { + Title = "Settings", + General = "General", + Toolbar = "Toolbar", + VideoPlayer = "Video player", + WaveformAndSpectrogram = "Waveform/spectrogram", + Tools = "Tools", + WordLists = "Word lists", + SsaStyle = "ASS/SSA Style", + Network = "Network", + NetworkSessionSettings = "Network session settings", + NetworkSessionNewSound = "Play sound file when new message arrives", + ShowToolBarButtons = "Show tool bar buttons", + New = "New", + Open = "Open", + Save = "Save", + SaveAs = "Save as", + Find = "Find", + Replace = "Replace", + VisualSync = "Visual sync", + SpellCheck = "Spell check", + SettingsName = "Settings", + Help = "Help", + ShowFrameRate = "Show frame rate in toolbar", + DefaultFrameRate = "Default frame rate", + DefaultFileEncoding = "Default file encoding", + AutoDetectAnsiEncoding = "Auto detect ANSI encoding", + SubtitleLineMaximumLength = "Single line max. length", + MaximumCharactersPerSecond = "Max. chars/sec", + AutoWrapWhileTyping = "Auto-wrap while typing", + DurationMinimumMilliseconds = "Min. duration, milliseconds", + DurationMaximumMilliseconds = "Max. duration, milliseconds", + MinimumGapMilliseconds = "Min. gap between subtitles in ms", + SubtitleFont = "Subtitle font", + SubtitleFontSize = "Subtitle font size", + SubtitleBold = "Bold", + SubtitleCenter = "Center", + SubtitleFontColor = "Subtitle font color", + SubtitleBackgroundColor = "Subtitle background color", + SpellChecker = "Spell checker", + RememberRecentFiles = "Show recent files (for reopen)", + StartWithLastFileLoaded = "Start with last file loaded", + RememberSelectedLine = "Remember selected line", + RememberPositionAndSize = "Remember main window position and size", + StartInSourceView = "Start in source view", + RemoveBlankLinesWhenOpening = "Remove blank lines when opening a subtitle", + ShowLineBreaksAs = "Show line breaks in list view as", + MainListViewDoubleClickAction = "Double-clicking line in main window list view will", + MainListViewNothing = "Nothing", + MainListViewVideoGoToPositionAndPause = "Go to video position and pause", + MainListViewVideoGoToPositionAndPlay = "Go to video position and play", + MainListViewEditText = "Go to edit text box", + MainListViewVideoGoToPositionMinus1SecAndPause = "Go to video position - 1 s and pause", + MainListViewVideoGoToPositionMinusHalfSecAndPause = "Go to video position - 0.5 s and pause", + MainListViewVideoGoToPositionMinus1SecAndPlay = "Go to video position - 1 s and play", + MainListViewEditTextAndPause = "Go to edit text box, and pause at video position", + AutoBackup = "Auto-backup", + AutoBackupEveryMinute = "Every minute", + AutoBackupEveryFiveMinutes = "Every 5th minute", + AutoBackupEveryFifteenMinutes = "Every 15th minute", + CheckForUpdates = "Check for updates", + AllowEditOfOriginalSubtitle = "Allow edit of original subtitle", + PromptDeleteLines = "Prompt for delete lines", + TimeCodeMode = "Time code mode", + TimeCodeModeHHMMSSMS = "HH:MM:SS.MS (00:00:01.500)", + TimeCodeModeHHMMSSFF = "HH:MM:SS:FF (00:00:01:12)", + VideoEngine = "Video engine", + DirectShow = "DirectShow", + DirectShowDescription = "quartz.dll in system32 folder", + ManagedDirectX = "Managed DirectX", + ManagedDirectXDescription = "Microsoft.DirectX.AudioVideoPlayback - .NET Managed code from DirectX", + MpcHc = "MPC-HC", + MpcHcDescription = "Media Player Classic - Home Cinema", + MPlayer = "MPlayer", + MPlayerDescription = "MPlayer2/Mplayer", + VlcMediaPlayer = "VLC media player", + VlcMediaPlayerDescription = "libvlc.dll from VLC media player 1.1.0 or newer", + VlcBrowseToLabel = "VLC path (only needed if you're using the portable version of VLC)", + ShowStopButton = "Show stop button", + ShowMuteButton = "Show mute button", + ShowFullscreenButton = "Show fullscreen button", + PreviewFontSize = "Subtitle preview font size", + MainWindowVideoControls = "Main window video controls", + CustomSearchTextAndUrl = "Custom search text and URL", + WaveformAppearance = "Waveform appearance", + WaveformGridColor = "Grid color", + WaveformShowGridLines = "Show grid lines", + ReverseMouseWheelScrollDirection = "Reverse mouse wheel scroll direction", + WaveformAllowOverlap = "Allow overlap (when moving/resizing)", + WaveformFocusMouseEnter = "Set focus on mouse enter", + WaveformListViewFocusMouseEnter = "Also set list view focus on mouse enter in list view", + WaveformBorderHitMs1 = "Border marker hit must be within", + WaveformBorderHitMs2 = "milliseconds", + WaveformColor = "Color", + WaveformSelectedColor = "Selected color", + WaveformBackgroundColor = "Back color", + WaveformTextColor = "Text color", + WaveformTextFontSize = "Text font size", + WaveformAndSpectrogramsFolderEmpty = "Empty 'Spectrograms' and 'Waveforms' folders", + WaveformAndSpectrogramsFolderInfo = "'Waveforms' and 'Spectrograms' folders contain {0} files ({1:0.00} MB)", + Spectrogram = "Spectrogram", + GenerateSpectrogram = "Generate spectrogram", + SpectrogramAppearance = "Spectrogram appearance", + SpectrogramOneColorGradient = "One color gradient", + SpectrogramClassic = "Classic", + WaveformUseFFmpeg = "Use FFmpeg for wave extraction", + WaveformFFmpegPath = "Path to FFmpeg", + WaveformBrowseToFFmpeg = "Browse to FFmpeg", + WaveformBrowseToVLC = "Browse to VLC portable", + SubStationAlphaStyle = "(Advanced) Sub Station Alpha style", + ChooseFont = "Choose font", + ChooseColor = "Choose color", + SsaOutline = "Outline", + SsaShadow = "Shadow", + SsaOpaqueBox = "Opaque box", + Testing123 = "Testing 123...", + Language = "Language", + NamesIgnoreLists = "Names/ignore list (case sensitive)", + AddNameEtc = "Add name", + AddWord = "Add word", + Remove = "Remove", + AddPair = "Add pair", + UserWordList = "User word list", + OcrFixList = "OCR fix list", + Location = "Location", + UseOnlineNamesEtc = "Use online names etc xml file", + WordAddedX = "Word added: {0}", + WordAlreadyExists = "Word already exists!", + RemoveX = "Remove {0}?", + WordNotFound = "Word not found", + CannotUpdateNamesEtcOnline = "Cannot update NamesEtc.xml online!", + ProxyServerSettings = "Proxy server settings", + ProxyAddress = "Proxy address", + ProxyAuthentication = "Authentication", + ProxyUserName = "User name", + ProxyPassword = "Password", + ProxyDomain = "Domain", + PlayXSecondsAndBack = "Play X seconds and back, X is", + StartSceneIndex = "Start scene paragraph is", + EndSceneIndex = "End scene paragraph is", + FirstPlusX = "First + {0}", + LastMinusX = "Last - {0}", + FixCommonerrors = "Fix common errors", + MergeLinesShorterThan = "Unbreak subtitles shorter than", + MusicSymbol = "Music symbol", + MusicSymbolsToReplace = "Music symbols to replace (separate by space)", + FixCommonOcrErrorsUseHardcodedRules = "Fix common OCR errors - also use hardcoded rules", + FixCommonerrorsFixShortDisplayTimesAllowMoveStartTime = "Fix short display time - allow move of start time", + Shortcuts = "Shortcuts", + Shortcut = "Shortcut", + Control = "Control", + Alt = "Alt", + Shift = "Shift", + Key = "Key", + TextBox = "Textbox", + UpdateShortcut = "Update", + ShortcutIsNotValid = "Shortcut is not valid: {0}", + ToggleDockUndockOfVideoControls = "Toggle dock/undock of video controls", + CreateSetEndAddNewAndGoToNew = "Set end, add new and go to new", + AdjustViaEndAutoStartAndGoToNext = "Adjust via end position and go to next", + AdjustSetEndTimeAndGoToNext = "Set end and go to next", + AdjustSetStartAutoDurationAndGoToNext = "Set start, auto duration and go to next", + AdjustSetEndNextStartAndGoToNext = "Set end, next start and go to next", + AdjustStartDownEndUpAndGoToNext = "Key down=set start, Key up=set end and go to next", + AdjustSelected100MsForward = "Move selected lines 100 ms forward", + AdjustSelected100MsBack = "Move selected lines 100 ms back", + AdjustSetStartTimeKeepDuration = "Set start time, keep duration", + AdjustSetEndAndOffsetTheRest = "Set end, offset the rest", + AdjustSetEndAndOffsetTheRestAndGoToNext = "Set end, offset the rest and go to next", + MainCreateStartDownEndUp = "Create new at key-down, set end time at key-up", + MergeDialog = "Merge dialog (insert dashes)", + GoToNext = "Go to next line", + GoToPrevious = "Go to previous line", + GoToCurrentSubtitleStart = "Go to current line start", + GoToCurrentSubtitleEnd = "Go to current line end", + ToggleFocus = "Toggle focus between list view and subtitle text box", + ToggleDialogDashes = "Toggle dialog dashes", + Alignment = "Alignment (selected lines)", + CopyTextOnly = "Copy text only to clip board (selected lines)", + CopyTextOnlyFromOriginalToCurrent = "Copy text from original to current", + AutoDurationSelectedLines = "Auto-duration (selected lines)", + ReverseStartAndEndingForRTL = "Reverse RTL start/end", + VerticalZoom = "Vertical zoom in", + VerticalZoomOut = "Vertical zoom out", + WaveformSeekSilenceForward = "Seek silence forward", + WaveformSeekSilenceBack = "Seek silence back", + WaveformAddTextHere = "Add text here (for new selection)", + WaveformPlayNewSelection = "Play new selection", + WaveformPlayFirstSelectedSubtitle = "Play first selected subtitle", + WaveformFocusListView = "Focus list view", + GoBack1Frame = "One frame back", + GoForward1Frame = "One frame forward", + GoBack100Milliseconds = "100 ms back", + GoForward100Milliseconds = "100 ms forward", + GoBack500Milliseconds = "500 ms back", + GoForward500Milliseconds = "500 ms forward", + GoBack1Second = "One second back", + GoForward1Second = "One second forward", + Pause = "Pause", + TogglePlayPause = "Toggle play/pause", + Fullscreen = "Fullscreen", + CustomSearch1 = "Translate, custom search 1", + CustomSearch2 = "Translate, custom search 2", + CustomSearch3 = "Translate, custom search 3", + CustomSearch4 = "Translate, custom search 4", + CustomSearch5 = "Translate, custom search 5", + CustomSearch6 = "Translate, custom search 6", + SyntaxColoring = "Syntax coloring", + ListViewSyntaxColoring = "List view syntax coloring", + SyntaxColorDurationIfTooSmall = "Color duration if too short", + SyntaxColorDurationIfTooLarge = "Color duration if too long", + SyntaxColorTextIfTooLong = "Color text if too long", + SyntaxColorTextMoreThanXLines = "Color text if more than lines:", + SyntaxColorOverlap = "Color time code overlap", + SyntaxErrorColor = "Error color", + GoToFirstSelectedLine = "Go to first selected line", + GoToNextEmptyLine = "Go to next empty line", + MergeSelectedLines = "Merge selected lines", + MergeSelectedLinesOnlyFirstText = "Merge selected lines, keep only first non-empty text", + ToggleTranslationMode = "Toggle translation mode", + SwitchOriginalAndTranslation = "Switch original and translation", + MergeOriginalAndTranslation = "Merge original and translation", + ShortcutIsAlreadyDefinedX = "Shortcut already defined: {0}", + ToggleTranslationAndOriginalInPreviews = "Toggle translation and original in video/audio preview", + ListViewColumnDelete = "Column, delete text", + ListViewColumnInsert = "Column, insert text", + ListViewColumnPaste = "Column, paste", + ListViewFocusWaveform = "Focus waveform/spectrogram", + ListViewGoToNextError = "Go to next error", + ShowBeamer = "Start subtitle fullscreen beamer", + MainTextBoxMoveLastWordDown = "Move last word down to next subtitle line", + MainTextBoxMoveFirstWordFromNextUp = "Move first word from next subtitle line up", + MainTextBoxSelectionToLower = "Selection to lowercase", + MainTextBoxSelectionToUpper = "Selection to uppercase", + MainTextBoxToggleAutoDuration = "Toggle auto duration", + MainTextBoxAutoBreak = "Auto break text", + MainTextBoxUnbreak = "Unbreak text", + MainFileSaveAll = "Save all", + Miscellaneous = "Misc.", + UseDoNotBreakAfterList = "Use do-not-break-after list (for auto-br)", + }; + + SetVideoOffset = new LanguageStructure.SetVideoOffset + { + Title = "Set video offset", + Description = "Set video offset (subtitles should not follow real video time, but e.g. +10 hours)", + RelativeToCurrentVideoPosition = "Relative to current video position" + }; + + ShowEarlierLater = new LanguageStructure.ShowEarlierLater + { + Title = "Show selected lines earlier/later", + TitleAll = "Show all lines earlier/later", + ShowEarlier = "Show earlier", + ShowLater = "Show later", + TotalAdjustmentX = "Total adjustment: {0}", + AllLines = "All lines", + SelectedLinesOnly = "Selected lines only", + SelectedLinesAndForward = "Selected line(s) and forward", + }; + + ShowHistory = new LanguageStructure.ShowHistory + { + Title = "History (for undo)", + SelectRollbackPoint = "Select time/description for rollback", + Time = "Time", + Description = "Description", + CompareHistoryItems = "Compare history items", + CompareWithCurrent = "Compare with current", + Rollback = "Rollback", + }; + + SpellCheck = new LanguageStructure.SpellCheck + { + Title = "Spell check", + FullText = "Full text", + WordNotFound = "Word not found", + Language = "Language", + Change = "Change", + ChangeAll = "Change all", + SkipOnce = "Skip &one", + SkipAll = "&Skip all", + AddToUserDictionary = "Add to user dictionary", + AddToNamesAndIgnoreList = "Add to names/noise list (case sensitive)", + AddToOcrReplaceList = "Add pair to OCR replace list", + Abort = "Abort", + Use = "Use", + UseAlways = "&Use always", + Suggestions = "Suggestions", + SpellCheckProgress = "Spell check [{0}] - {1}", + EditWholeText = "Edit whole text", + EditWordOnly = "Edit word only", + AddXToNamesEtc = "Add '{0}' to names/etc list", + AutoFixNames = "Auto fix names where only casing differ", + CheckOneLetterWords = "Prompt for unknown one letter words", + TreatINQuoteAsING = "Treat word ending \" in' \" as \" ing \" (English only)", + ImageText = "Image text", + SpellCheckCompleted = "Spell check completed", + SpellCheckAborted = "Spell check aborted", + UndoX = "Undo: {0}", + }; + + Split = new LanguageStructure.Split + { + Title = "Split", + SplitOptions = "Split options", + Lines = "Lines", + Characters = "Characters", + NumberOfEqualParts = "Number of equal parts", + SubtitleInfo = "Subtitle info", + NumberOfLinesX = "Number of lines: {0:#,###}", + NumberOfCharactersX = "Number of characters: {0:#,###,###}", + Output = "Output", + FileName = "File name", + OutputFolder = "Output folder", + DoSplit = "Split", + Basic = "Basic", + }; + + SplitLongLines = new LanguageStructure.SplitLongLines + { + Title = "Split long lines", + SingleLineMaximumLength = "Single line maximum length", + LineMaximumLength = "Line maximum length", + LineContinuationBeginEndStrings = "Line continuation begin/end strings", + NumberOfSplits = "Number of splits: {0}", + LongestSingleLineIsXAtY = "Longest single line length is {0} at line {1}", + LongestLineIsXAtY = "Longest total line length is {0} at line {1}", + }; + + SplitSubtitle = new LanguageStructure.SplitSubtitle + { + Title = "Split subtitle", + Description1 = "Enter length of first part of video or browse", + Description2 = "and get length from video file:", + Part1 = "Part1", + Part2 = "Part2", + Done = "&Done", + Split = "&Split", + SavePartOneAs = "Save part 1 as...", + SavePartTwoAs = "Save part 2 as...", + NothingToSplit = "Nothing to split!", + UnableToSaveFileX = "Unable to save {0}", + OverwriteExistingFiles = "Overwrite existing files?", + FolderNotFoundX = "Folder not found: {0}", + Untitled = "Untitled", + }; + + StartNumberingFrom = new LanguageStructure.StartNumberingFrom + { + Title = "Renumber", + StartFromNumber = "Start from number:", + PleaseEnterAValidNumber = "Ups, please enter a number", + }; + + Statistics = new LanguageStructure.Statistics + { + Title = "Statistics", + TitleWithFileName = "Statistics - {0}", + GeneralStatistics = "General statistics", + NothingFound = "Nothing found", + MostUsed = "Most used...", + MostUsedWords = "Most used words", + MostUsedLines = "Most used lines", + NumberOfLinesX = "Number of subtitle lines: {0:#,###}", + LengthInFormatXinCharactersY = "Number of characters as {0}: {1:#,###,##0}", + NumberOfCharactersInTextOnly = "Number of characters in text only: {0:#,###,##0}", + NumberOfItalicTags = "Number of italic tags: {0}", + TotalCharsPerSecond = "Total characters/second: {0:0.0} seconds", + NumberOfBoldTags = "Number of bold tags: {0}", + NumberOfUnderlineTags = "Number of underline tags: {0}", + NumberOfFontTags = "Number of font tags: {0}", + NumberOfAlignmentTags = "Number of alignment tags: {0}", + LineLengthMinimum = "Subtitle length - minimum: {0}", + LineLengthMaximum = "Subtitle length - maximum: {0}", + LineLengthAverage = "Subtitle length - average: {0}", + LinesPerSubtitleAverage = "Subtitle, number of lines - average: {0:0.0}", + SingleLineLengthMinimum = "Single line length - minimum: {0}", + SingleLineLengthMaximum = "Single line length - maximum: {0}", + SingleLineLengthAverage = "Single line length - average: {0}", + DurationMinimum = "Duration - minimum: {0:0.000} seconds", + DurationMaximum = "Duration - maximum: {0:0.000} seconds", + DurationAverage = "Duration - average: {0:0.000} seconds", + CharactersPerSecondMinimum = "Characters/sec - minimum: {0:0.000}", + CharactersPerSecondMaximum = "Characters/sec - maximum: {0:0.000}", + CharactersPerSecondAverage = "Characters/sec - average: {0:0.000}", + Export = "Export...", + }; + + SubStationAlphaProperties = new LanguageStructure.SubStationAlphaProperties + { + Title = "Advanced Sub Station Alpha properties", + TitleSubstationAlpha = "Sub Station Alpha properties", + Script = "Script", + ScriptTitle = "Title", + OriginalScript = "Original script", + Translation = "Translation", + Editing = "Editing", + Timing = "Timing", + SyncPoint = "Sync point", + UpdatedBy = "Updated by", + UpdateDetails = "Update details", + Resolution = "Resolution", + VideoResolution = "Video resolution", + Options = "Options", + WrapStyle = "Wrap style", + Collision = "Collision", + ScaleBorderAndShadow = "Scale border and shadow", + }; + + SubStationAlphaStyles = new LanguageStructure.SubStationAlphaStyles + { + Title = "Advanced Sub Station Alpha styles", + TitleSubstationAlpha = "Sub Station Alpha styles", + Styles = "Styles", + Properties = "Properties", + Name = "Name", + Font = "Font", + FontName = "Font name", + FontSize = "Font size", + UseCount = "Used", + Primary = "Primary", + Secondary = "Secondary", + Tertiary = "Tertiary", + Outline = "Outline", + Shadow = "Shadow", + Back = "Back", + Alignment = "Alignment", + TopLeft = "Top/left", + TopCenter = "Top/center", + TopRight = "Top/right", + MiddleLeft = "Middle/left", + MiddleCenter = "Middle/center", + MiddleRight = "Middle/right", + BottomLeft = "Bottom/left", + BottomCenter = "Bottom/center", + BottomRight = "Bottom/right", + Colors = "Colors", + Margins = "Margins", + MarginLeft = "Margin left", + MarginRight = "Margin right", + MarginVertical = "Margin vertical", + Border = "Border", + PlusShadow = "+ Shadow", + OpaqueBox = "Opaque box (uses outline color)", + Import = "Import...", + Export = "Export...", + New = "New", + Copy = "Copy", + CopyOfY = "Copy of {0}", + CopyXOfY = "Copy {0} of {1}", + Remove = "Remove", + RemoveAll = "Remove all", + ImportStyleFromFile = "Import style from file...", + ExportStyleToFile = "Export style to file... (will add style if file already exists)", + ChooseStyle = "Choose style to import", + StyleAlreadyExits = "Style already exists: {0}", + StyleXExportedToFileY = "Style '{0}' exported to file '{1}'", + StyleXImportedFromFileY = "Style '{0}' imported from file '{1}'", + }; + + PointSync = new LanguageStructure.PointSync + { + Title = "Point synchronization", + TitleViaOtherSubtitle = "Point sync via other subtitle", + SyncHelp = "Set at least two sync points to make rough synchronization", + SetSyncPoint = "Set sync point", + RemoveSyncPoint = "Remove sync point", + SyncPointsX = "Sync points: {0}", + Info = "One sync point will adjust position, two or more sync points will adjust position and speed", + ApplySync = "Apply", + }; + + TransportStreamSubtitleChooser = new LanguageStructure.TransportStreamSubtitleChooser + { + Title = "Transport stream subtitle chooser - {0}", + PidLine = "Transport Packet Identifier (PID) = {0}, number of subtitles = {1}", + SubLine = "{0}: {1} -> {2}, {3} image(s)", + }; + + UnknownSubtitle = new LanguageStructure.UnknownSubtitle + { + Title = "Unknown subtitle type", + Message = "If you want this fixed please send an email to mailto:niksedk@gmail.com and include a copy of the subtitle.", + }; + + VisualSync = new LanguageStructure.VisualSync + { + Title = "Visual sync", + StartScene = "Start scene", + EndScene = "End scene", + Synchronize = "Sync", + HalfASecondBack = "< ½ s", + ThreeSecondsBack = "< 3 s", + PlayXSecondsAndBack = "Play {0} s and back", + FindText = "Find text", + GoToSubPosition = "Go to sub pos", + KeepChangesTitle = "Keep changes?", + KeepChangesMessage = @"Changes have been made to subtitle in 'Visual sync'. + +Keep changes?", + SynchronizationDone = "Sync done!", + StartSceneMustComeBeforeEndScene = "Start scene must come before end scene!", + Tip = "Tip: Use keys to move 100 ms back/forward", + }; + + VobSubEditCharacters = new LanguageStructure.VobSubEditCharacters + { + Title = "Edit image compare database", + ChooseCharacter = "Choose character(s)", + ImageCompareFiles = "Image compare files", + CurrentCompareImage = "Current compare image", + TextAssociatedWithImage = "Text associated with image", + IsItalic = "Is &italic", + Update = "&Update", + Delete = "&Delete", + ImageDoubleSize = "Image double size", + ImageFileNotFound = "Image file not found", + Image = "Image", + }; + + VobSubOcr = new LanguageStructure.VobSubOcr + { + Title = "Import/OCR VobSub (sub/idx) subtitle", + TitleBluRay = "Import/OCR Blu-ray (.sup) subtitle", + OcrMethod = "OCR method", + OcrViaModi = "OCR via Microsoft Office Document Imaging (MODI). Requires Microsoft Office", + Language = "Language", + OcrViaImageCompare = "OCR via image compare", + ImageDatabase = "Image database", + NoOfPixelsIsSpace = "No of pixels is space", + MaxErrorPercent = "Max. error%", + New = "New", + Edit = "Edit", + StartOcr = "Start OCR", + Stop = "Stop", + StartOcrFrom = "Start OCR from subtitle no:", + LoadingVobSubImages = "Loading VobSub images...", + LoadingImageCompareDatabase = "Loading image compare database...", + ConvertingImageCompareDatabase = "Converting image compare database to new format (images.db/images.xml)...", + SubtitleImage = "Subtitle image", + SubtitleText = "Subtitle text", + UnableToCreateCharacterDatabaseFolder = "Unable to create 'Character database folder': {0}", + SubtitleImageXofY = "Subtitle image {0} of {1}", + ImagePalette = "Image palette", + UseCustomColors = "Use custom colors", + Transparent = "Transparent", + TransparentMinAlpha = "Min. alpha value (0=transparent, 255=fully visible)", + TransportStream = "Transport stream", + TransportStreamGrayscale = "Grayscale", + TransportStreamGetColor = "Use color (will include some splitting of lines)", + PromptForUnknownWords = "Prompt for unknown words", + TryToGuessUnkownWords = "Try to guess unknown words", + AutoBreakSubtitleIfMoreThanTwoLines = "Auto break paragraph if more than two lines", + AllFixes = "All fixes", + GuessesUsed = "Guesses used", + UnknownWords = "Unknown words", + OcrAutoCorrectionSpellChecking = "OCR auto correction / spell checking", + OcrViaTesseract = "OCR via Tesseract", + OcrViaNOCR = "OCR via nOCR", + FixOcrErrors = "Fix OCR errors", + ImportTextWithMatchingTimeCodes = "Import text with matching time codes...", + ImportNewTimeCodes = "Import new time codes", + SaveSubtitleImageAs = "Save subtitle image as...", + SaveAllSubtitleImagesAsBdnXml = "Save all images (png/bdn xml)...", + SaveAllSubtitleImagesWithHtml = "Save all images with HTML index...", + XImagesSavedInY = "{0} images saved in {1}", + TryModiForUnknownWords = "Try Microsoft MODI OCR for unknown words", + DictionaryX = "Dictionary: {0}", + RightToLeft = "Right to left", + ShowOnlyForcedSubtitles = "Show only forced subtitles", + UseTimeCodesFromIdx = "Use time codes from .idx file", + NoMatch = "", + AutoTransparentBackground = "Auto transparent background", + InspectCompareMatchesForCurrentImage = "Inspect compare matches for current image...", + EditLastAdditions = "Edit last image compare additions...", + SetUnitalicFactor = "Set un-italic factor...", + DiscardTitle = "Discard changes made in OCR?", + DiscardText = "Do you want to discard changes made in current OCR session?", + }; + + VobSubOcrCharacter = new LanguageStructure.VobSubOcrCharacter + { + Title = "VobSub - Manual image to text", + Abort = "&Abort", + Skip = "&Skip", + SubtitleImage = "Subtitle image", + ShrinkSelection = "Shrink selection", + ExpandSelection = "Expand selection", + Characters = "Character(s)", + CharactersAsText = "Character(s) as text", + Italic = "&Italic", + Nordic = "Nordic", + Spanish = "Spanish", + German = "German", + AutoSubmitOnFirstChar = "Auto submit on &first char", + EditLastX = "Edit last: {0}", + }; + + VobSubOcrCharacterInspect = new LanguageStructure.VobSubOcrCharacterInspect + { + Title = "Inspect compare matches for current image", + InspectItems = "Inspect items", + AddBetterMatch = "Add better match", + }; + + VobSubOcrNewFolder = new LanguageStructure.VobSubOcrNewFolder + { + Title = "New folder", + Message = "Name of new character database folder", + }; + + VobSubOcrSetItalicFactor = new LanguageStructure.VobSubOcrSetItalicFactor + { + Title = "Set un-italic factor", + Description = "Adjust value until text style is normal and not italic. Note that original image should be italic.", + }; + + Waveform = new LanguageStructure.Waveform + { + ClickToAddWaveform = "Click to add waveform", + ClickToAddWaveformAndSpectrogram = "Click to add waveform/spectrogram", + Seconds = "seconds", + ZoomIn = "Zoom in", + ZoomOut = "Zoom out", + AddParagraphHere = "Add text here", + AddParagraphHereAndPasteText = "Add text from clipboard here", + FocusTextBox = "Focus text box", + DeleteParagraph = "Delete text", + Split = "Split", + SplitAtCursor = "Split at cursor", + MergeWithPrevious = "Merge with previous", + MergeWithNext = "Merge with next", + PlaySelection = "Play selection", + ShowWaveformAndSpectrogram = "Show waveform and spectrogram", + ShowWaveformOnly = "Show waveform only", + ShowSpectrogramOnly = "Show spectrogram only", + SeekSilence = "Seek silence...", + GuessTimeCodes = "Guess time codes...", + }; + + WaveformGenerateTimeCodes = new LanguageStructure.WaveformGenerateTimeCodes + { + Title = "Guess time codes", + StartFrom = "Start from", + CurrentVideoPosition = "Current video position", + Beginning = "Beginning", + DeleteLines = "Delete lines", + FromCurrentVideoPosition = "From current video position", + DetectOptions = "Detect options", + ScanBlocksOfMs = "Scan blocks of milliseconds", + BlockAverageVolMin1 = "Block average volume must be above", + BlockAverageVolMin2 = "% of total average volume", + BlockAverageVolMax1 = "Block average volume must be below", + BlockAverageVolMax2 = "% of total max volume", + SplitLongLinesAt1 = "Split long subtitles at", + SplitLongLinesAt2 = "milliseconds", + Other = "Other", + }; + + WebVttNewVoice = new LanguageStructure.WebVttNewVoice + { + Title = "WebVTT - set new voice", + VoiceName = "Name of voice", + }; + } + + //public static Language Load(StreamReader sr) // normal but slow .net way + //{ + // var s = new XmlSerializer(typeof(Language)); + // var language = (Language)s.Deserialize(sr); + // return language; + //} + + public static Language Load(string fileName) + { + var language = LanguageDeserializer.CustomDeserializeLanguage(fileName); + var english = new Language(); + + // Use alternative, if translated (Forms/Main.cs) + if (language.Main.Menu.Tools.Number == english.Main.Menu.Tools.Number && language.General.Number != english.General.Number) + language.Main.Menu.Tools.Number = language.General.Number; + if (language.Main.Menu.Tools.EndTime == english.Main.Menu.Tools.EndTime && language.General.EndTime != english.General.EndTime) + language.Main.Menu.Tools.EndTime = language.General.EndTime; + if (language.Main.Menu.Tools.Duration == english.Main.Menu.Tools.Duration && language.General.Duration != english.General.Duration) + language.Main.Menu.Tools.Duration = language.General.Duration; + if (language.Main.Menu.Tools.StartTime == english.Main.Menu.Tools.StartTime && language.General.StartTime != english.General.StartTime) + language.Main.Menu.Tools.StartTime = language.General.StartTime; + if (language.Main.BeforeMergeLinesWithSameText == english.Main.BeforeMergeLinesWithSameText && language.Main.BeforeMergeShortLines != english.Main.BeforeMergeShortLines) + language.Main.BeforeMergeLinesWithSameText = language.Main.BeforeMergeShortLines; + // Use alternative, if translated (Forms/Settings.cs) + if (language.Settings.AdjustSetEndTimeAndGoToNext == english.Settings.AdjustSetEndTimeAndGoToNext && language.Main.VideoControls.SetEndTimeAndGoToNext != english.Main.VideoControls.SetEndTimeAndGoToNext) + language.Settings.AdjustSetEndTimeAndGoToNext = language.Main.VideoControls.SetEndTimeAndGoToNext; + // Translated alternative without format item (../Forms/PluginsGet.cs) + if (language.PluginsGet.UpdateAllX == english.PluginsGet.UpdateAllX && language.PluginsGet.UpdateAll != english.PluginsGet.UpdateAll) + language.PluginsGet.UpdateAllX = null; + + return language; + } + + public string GetCurrentLanguageAsXml() + { + var s = new XmlSerializer(typeof(Language)); + var sb = new StringBuilder(); + var w = new StringWriter(sb); + s.Serialize(w, this); + w.Close(); + + string xml = sb.ToString(); + xml = xml.Replace("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" ", string.Empty); + xml = xml.Replace("xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ", string.Empty); + xml = xml.Replace("encoding=\"utf-16\"", "encoding=\"utf-8\""); + xml = xml.Replace(" ", "Translated by Nikse"); + return xml.Trim(); + } + + public void Save(string fileName) + { + File.WriteAllText(fileName, GetCurrentLanguageAsXml(), Encoding.UTF8); + } + + } +} \ No newline at end of file diff --git a/libse/LanguageDeserializer.cs b/libse/LanguageDeserializer.cs new file mode 100644 index 000000000..4a2f35e33 --- /dev/null +++ b/libse/LanguageDeserializer.cs @@ -0,0 +1,5663 @@ +using System.IO; +using System.Text; +using System.Xml; + +// !!! THIS FILE IS AUTO-GENERATED!!! +// !!! THIS FILE IS AUTO-GENERATED!!! +// !!! THIS FILE IS AUTO-GENERATED!!! + +namespace Nikse.SubtitleEdit.Core +{ + + public class LanguageDeserializer // NOTE: This class is AUTO-GENERATED!!!! + { + + public static Language CustomDeserializeLanguage(string fileName) + { + var name = new StringBuilder(100, 1000); + var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + var language = new Language(); + + using (XmlReader reader = XmlReader.Create(stream, new XmlReaderSettings { + IgnoreWhitespace = true, IgnoreProcessingInstructions = true, IgnoreComments = true, + DtdProcessing = DtdProcessing.Ignore, CheckCharacters = false, CloseInput = true })) + { + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + if (!reader.IsEmptyElement && reader.Depth > 0) + name.Append('/').Append(reader.Name); + else if (reader.Depth == 0) + language.Name = reader["Name"]; + } + else if (reader.NodeType == XmlNodeType.EndElement) + { + if (name.Length > 0) + name.Length -= reader.Name.Length + 1; + } + else if (reader.NodeType == XmlNodeType.Text) + { + SetValue(language, reader, name.ToString(1, name.Length - 1)); + } + } + } + return language; + } + + private static void SetValue(Language language, XmlReader reader, string name) + { + switch (name) + { + case "Name": + language.Name = reader.Value; + break; + case "General/Title": + language.General.Title = reader.Value; + break; + case "General/Version": + language.General.Version = reader.Value; + break; + case "General/TranslatedBy": + language.General.TranslatedBy = reader.Value; + break; + case "General/CultureName": + language.General.CultureName = reader.Value; + break; + case "General/HelpFile": + language.General.HelpFile = reader.Value; + break; + case "General/Ok": + language.General.Ok = reader.Value; + break; + case "General/Cancel": + language.General.Cancel = reader.Value; + break; + case "General/Apply": + language.General.Apply = reader.Value; + break; + case "General/None": + language.General.None = reader.Value; + break; + case "General/All": + language.General.All = reader.Value; + break; + case "General/Preview": + language.General.Preview = reader.Value; + break; + case "General/SubtitleFiles": + language.General.SubtitleFiles = reader.Value; + break; + case "General/AllFiles": + language.General.AllFiles = reader.Value; + break; + case "General/VideoFiles": + language.General.VideoFiles = reader.Value; + break; + case "General/AudioFiles": + language.General.AudioFiles = reader.Value; + break; + case "General/OpenSubtitle": + language.General.OpenSubtitle = reader.Value; + break; + case "General/OpenVideoFile": + language.General.OpenVideoFile = reader.Value; + break; + case "General/OpenVideoFileTitle": + language.General.OpenVideoFileTitle = reader.Value; + break; + case "General/NoVideoLoaded": + language.General.NoVideoLoaded = reader.Value; + break; + case "General/VideoInformation": + language.General.VideoInformation = reader.Value; + break; + case "General/PositionX": + language.General.PositionX = reader.Value; + break; + case "General/StartTime": + language.General.StartTime = reader.Value; + break; + case "General/EndTime": + language.General.EndTime = reader.Value; + break; + case "General/Duration": + language.General.Duration = reader.Value; + break; + case "General/NumberSymbol": + language.General.NumberSymbol = reader.Value; + break; + case "General/Number": + language.General.Number = reader.Value; + break; + case "General/Text": + language.General.Text = reader.Value; + break; + case "General/HourMinutesSecondsMilliseconds": + language.General.HourMinutesSecondsMilliseconds = reader.Value; + break; + case "General/Bold": + language.General.Bold = reader.Value; + break; + case "General/Italic": + language.General.Italic = reader.Value; + break; + case "General/Underline": + language.General.Underline = reader.Value; + break; + case "General/Visible": + language.General.Visible = reader.Value; + break; + case "General/FrameRate": + language.General.FrameRate = reader.Value; + break; + case "General/Name": + language.General.Name = reader.Value; + break; + case "General/FileNameXAndSize": + language.General.FileNameXAndSize = reader.Value; + break; + case "General/ResolutionX": + language.General.ResolutionX = reader.Value; + break; + case "General/FrameRateX": + language.General.FrameRateX = reader.Value; + break; + case "General/TotalFramesX": + language.General.TotalFramesX = reader.Value; + break; + case "General/VideoEncodingX": + language.General.VideoEncodingX = reader.Value; + break; + case "General/SingleLineLengths": + language.General.SingleLineLengths = reader.Value; + break; + case "General/TotalLengthX": + language.General.TotalLengthX = reader.Value; + break; + case "General/TotalLengthXSplitLine": + language.General.TotalLengthXSplitLine = reader.Value; + break; + case "General/SplitLine": + language.General.SplitLine = reader.Value; + break; + case "General/NotAvailable": + language.General.NotAvailable = reader.Value; + break; + case "General/OverlapPreviousLineX": + language.General.OverlapPreviousLineX = reader.Value; + break; + case "General/OverlapX": + language.General.OverlapX = reader.Value; + break; + case "General/OverlapNextX": + language.General.OverlapNextX = reader.Value; + break; + case "General/Negative": + language.General.Negative = reader.Value; + break; + case "General/RegularExpressionIsNotValid": + language.General.RegularExpressionIsNotValid = reader.Value; + break; + case "General/SubtitleSaved": + language.General.SubtitleSaved = reader.Value; + break; + case "General/CurrentSubtitle": + language.General.CurrentSubtitle = reader.Value; + break; + case "General/OriginalText": + language.General.OriginalText = reader.Value; + break; + case "General/OpenOriginalSubtitleFile": + language.General.OpenOriginalSubtitleFile = reader.Value; + break; + case "General/PleaseWait": + language.General.PleaseWait = reader.Value; + break; + case "General/SessionKey": + language.General.SessionKey = reader.Value; + break; + case "General/UserName": + language.General.UserName = reader.Value; + break; + case "General/UserNameAlreadyInUse": + language.General.UserNameAlreadyInUse = reader.Value; + break; + case "General/WebServiceUrl": + language.General.WebServiceUrl = reader.Value; + break; + case "General/IP": + language.General.IP = reader.Value; + break; + case "General/VideoWindowTitle": + language.General.VideoWindowTitle = reader.Value; + break; + case "General/AudioWindowTitle": + language.General.AudioWindowTitle = reader.Value; + break; + case "General/ControlsWindowTitle": + language.General.ControlsWindowTitle = reader.Value; + break; + case "General/Advanced": + language.General.Advanced = reader.Value; + break; + case "General/Style": + language.General.Style = reader.Value; + break; + case "General/StyleLanguage": + language.General.StyleLanguage = reader.Value; + break; + case "General/Character": + language.General.Character = reader.Value; + break; + case "General/Class": + language.General.Class = reader.Value; + break; + case "General/GeneralText": + language.General.GeneralText = reader.Value; + break; + case "General/LineNumber": + language.General.LineNumber = reader.Value; + break; + case "General/Before": + language.General.Before = reader.Value; + break; + case "General/After": + language.General.After = reader.Value; + break; + case "General/Size": + language.General.Size = reader.Value; + break; + case "About/Title": + language.About.Title = reader.Value; + break; + case "About/AboutText1": + language.About.AboutText1 = reader.Value; + break; + case "AddToNames/Title": + language.AddToNames.Title = reader.Value; + break; + case "AddToNames/Description": + language.AddToNames.Description = reader.Value; + break; + case "AddToOcrReplaceList/Title": + language.AddToOcrReplaceList.Title = reader.Value; + break; + case "AddToOcrReplaceList/Description": + language.AddToOcrReplaceList.Description = reader.Value; + break; + case "AddToUserDictionary/Title": + language.AddToUserDictionary.Title = reader.Value; + break; + case "AddToUserDictionary/Description": + language.AddToUserDictionary.Description = reader.Value; + break; + case "AddWaveform/Title": + language.AddWaveform.Title = reader.Value; + break; + case "AddWaveform/SourceVideoFile": + language.AddWaveform.SourceVideoFile = reader.Value; + break; + case "AddWaveform/GenerateWaveformData": + language.AddWaveform.GenerateWaveformData = reader.Value; + break; + case "AddWaveform/PleaseWait": + language.AddWaveform.PleaseWait = reader.Value; + break; + case "AddWaveform/VlcMediaPlayerNotFoundTitle": + language.AddWaveform.VlcMediaPlayerNotFoundTitle = reader.Value; + break; + case "AddWaveform/VlcMediaPlayerNotFound": + language.AddWaveform.VlcMediaPlayerNotFound = reader.Value; + break; + case "AddWaveform/GoToVlcMediaPlayerHomePage": + language.AddWaveform.GoToVlcMediaPlayerHomePage = reader.Value; + break; + case "AddWaveform/GeneratingPeakFile": + language.AddWaveform.GeneratingPeakFile = reader.Value; + break; + case "AddWaveform/GeneratingSpectrogram": + language.AddWaveform.GeneratingSpectrogram = reader.Value; + break; + case "AddWaveform/ExtractingSeconds": + language.AddWaveform.ExtractingSeconds = reader.Value; + break; + case "AddWaveform/ExtractingMinutes": + language.AddWaveform.ExtractingMinutes = reader.Value; + break; + case "AddWaveform/WaveFileNotFound": + language.AddWaveform.WaveFileNotFound = reader.Value; + break; + case "AddWaveform/WaveFileMalformed": + language.AddWaveform.WaveFileMalformed = reader.Value; + break; + case "AddWaveform/LowDiskSpace": + language.AddWaveform.LowDiskSpace = reader.Value; + break; + case "AddWaveform/FreeDiskSpace": + language.AddWaveform.FreeDiskSpace = reader.Value; + break; + case "AddWaveformBatch/Title": + language.AddWaveformBatch.Title = reader.Value; + break; + case "AddWaveformBatch/ExtractingAudio": + language.AddWaveformBatch.ExtractingAudio = reader.Value; + break; + case "AddWaveformBatch/Calculating": + language.AddWaveformBatch.Calculating = reader.Value; + break; + case "AddWaveformBatch/Done": + language.AddWaveformBatch.Done = reader.Value; + break; + case "AddWaveformBatch/Error": + language.AddWaveformBatch.Error = reader.Value; + break; + case "AdjustDisplayDuration/Title": + language.AdjustDisplayDuration.Title = reader.Value; + break; + case "AdjustDisplayDuration/AdjustVia": + language.AdjustDisplayDuration.AdjustVia = reader.Value; + break; + case "AdjustDisplayDuration/Seconds": + language.AdjustDisplayDuration.Seconds = reader.Value; + break; + case "AdjustDisplayDuration/Percent": + language.AdjustDisplayDuration.Percent = reader.Value; + break; + case "AdjustDisplayDuration/Recalculate": + language.AdjustDisplayDuration.Recalculate = reader.Value; + break; + case "AdjustDisplayDuration/AddSeconds": + language.AdjustDisplayDuration.AddSeconds = reader.Value; + break; + case "AdjustDisplayDuration/SetAsPercent": + language.AdjustDisplayDuration.SetAsPercent = reader.Value; + break; + case "AdjustDisplayDuration/Note": + language.AdjustDisplayDuration.Note = reader.Value; + break; + case "AdjustDisplayDuration/PleaseSelectAValueFromTheDropDownList": + language.AdjustDisplayDuration.PleaseSelectAValueFromTheDropDownList = reader.Value; + break; + case "AdjustDisplayDuration/PleaseChoose": + language.AdjustDisplayDuration.PleaseChoose = reader.Value; + break; + case "ApplyDurationLimits/Title": + language.ApplyDurationLimits.Title = reader.Value; + break; + case "ApplyDurationLimits/FixesAvailable": + language.ApplyDurationLimits.FixesAvailable = reader.Value; + break; + case "ApplyDurationLimits/UnableToFix": + language.ApplyDurationLimits.UnableToFix = reader.Value; + break; + case "AutoBreakUnbreakLines/TitleAutoBreak": + language.AutoBreakUnbreakLines.TitleAutoBreak = reader.Value; + break; + case "AutoBreakUnbreakLines/TitleUnbreak": + language.AutoBreakUnbreakLines.TitleUnbreak = reader.Value; + break; + case "AutoBreakUnbreakLines/LinesFoundX": + language.AutoBreakUnbreakLines.LinesFoundX = reader.Value; + break; + case "AutoBreakUnbreakLines/OnlyBreakLinesLongerThan": + language.AutoBreakUnbreakLines.OnlyBreakLinesLongerThan = reader.Value; + break; + case "AutoBreakUnbreakLines/OnlyUnbreakLinesLongerThan": + language.AutoBreakUnbreakLines.OnlyUnbreakLinesLongerThan = reader.Value; + break; + case "BatchConvert/Title": + language.BatchConvert.Title = reader.Value; + break; + case "BatchConvert/Input": + language.BatchConvert.Input = reader.Value; + break; + case "BatchConvert/InputDescription": + language.BatchConvert.InputDescription = reader.Value; + break; + case "BatchConvert/Status": + language.BatchConvert.Status = reader.Value; + break; + case "BatchConvert/Output": + language.BatchConvert.Output = reader.Value; + break; + case "BatchConvert/ChooseOutputFolder": + language.BatchConvert.ChooseOutputFolder = reader.Value; + break; + case "BatchConvert/OverwriteExistingFiles": + language.BatchConvert.OverwriteExistingFiles = reader.Value; + break; + case "BatchConvert/Style": + language.BatchConvert.Style = reader.Value; + break; + case "BatchConvert/ConvertOptions": + language.BatchConvert.ConvertOptions = reader.Value; + break; + case "BatchConvert/RemoveFormatting": + language.BatchConvert.RemoveFormatting = reader.Value; + break; + case "BatchConvert/RemoveTextForHI": + language.BatchConvert.RemoveTextForHI = reader.Value; + break; + case "BatchConvert/OverwriteOriginalFiles": + language.BatchConvert.OverwriteOriginalFiles = reader.Value; + break; + case "BatchConvert/RedoCasing": + language.BatchConvert.RedoCasing = reader.Value; + break; + case "BatchConvert/Convert": + language.BatchConvert.Convert = reader.Value; + break; + case "BatchConvert/NothingToConvert": + language.BatchConvert.NothingToConvert = reader.Value; + break; + case "BatchConvert/PleaseChooseOutputFolder": + language.BatchConvert.PleaseChooseOutputFolder = reader.Value; + break; + case "BatchConvert/NotConverted": + language.BatchConvert.NotConverted = reader.Value; + break; + case "BatchConvert/Converted": + language.BatchConvert.Converted = reader.Value; + break; + case "BatchConvert/ConvertedX": + language.BatchConvert.ConvertedX = reader.Value; + break; + case "BatchConvert/Settings": + language.BatchConvert.Settings = reader.Value; + break; + case "BatchConvert/SplitLongLines": + language.BatchConvert.SplitLongLines = reader.Value; + break; + case "BatchConvert/AutoBalance": + language.BatchConvert.AutoBalance = reader.Value; + break; + case "BatchConvert/ScanFolder": + language.BatchConvert.ScanFolder = reader.Value; + break; + case "BatchConvert/ScanningFolder": + language.BatchConvert.ScanningFolder = reader.Value; + break; + case "BatchConvert/Recursive": + language.BatchConvert.Recursive = reader.Value; + break; + case "BatchConvert/SetMinMsBetweenSubtitles": + language.BatchConvert.SetMinMsBetweenSubtitles = reader.Value; + break; + case "BatchConvert/PlainText": + language.BatchConvert.PlainText = reader.Value; + break; + case "BatchConvert/Ocr": + language.BatchConvert.Ocr = reader.Value; + break; + case "BatchConvert/Filter": + language.BatchConvert.Filter = reader.Value; + break; + case "BatchConvert/FilterSkipped": + language.BatchConvert.FilterSkipped = reader.Value; + break; + case "BatchConvert/FilterSrtNoUtf8BOM": + language.BatchConvert.FilterSrtNoUtf8BOM = reader.Value; + break; + case "BatchConvert/FilterMoreThanTwoLines": + language.BatchConvert.FilterMoreThanTwoLines = reader.Value; + break; + case "BatchConvert/FilterContains": + language.BatchConvert.FilterContains = reader.Value; + break; + case "BatchConvert/FixCommonErrorsErrorX": + language.BatchConvert.FixCommonErrorsErrorX = reader.Value; + break; + case "BatchConvert/MultipleReplaceErrorX": + language.BatchConvert.MultipleReplaceErrorX = reader.Value; + break; + case "BatchConvert/AutoBalanceErrorX": + language.BatchConvert.AutoBalanceErrorX = reader.Value; + break; + case "Beamer/Title": + language.Beamer.Title = reader.Value; + break; + case "ChangeCasing/Title": + language.ChangeCasing.Title = reader.Value; + break; + case "ChangeCasing/ChangeCasingTo": + language.ChangeCasing.ChangeCasingTo = reader.Value; + break; + case "ChangeCasing/NormalCasing": + language.ChangeCasing.NormalCasing = reader.Value; + break; + case "ChangeCasing/FixNamesCasing": + language.ChangeCasing.FixNamesCasing = reader.Value; + break; + case "ChangeCasing/FixOnlyNamesCasing": + language.ChangeCasing.FixOnlyNamesCasing = reader.Value; + break; + case "ChangeCasing/OnlyChangeAllUppercaseLines": + language.ChangeCasing.OnlyChangeAllUppercaseLines = reader.Value; + break; + case "ChangeCasing/AllUppercase": + language.ChangeCasing.AllUppercase = reader.Value; + break; + case "ChangeCasing/AllLowercase": + language.ChangeCasing.AllLowercase = reader.Value; + break; + case "ChangeCasingNames/Title": + language.ChangeCasingNames.Title = reader.Value; + break; + case "ChangeCasingNames/NamesFoundInSubtitleX": + language.ChangeCasingNames.NamesFoundInSubtitleX = reader.Value; + break; + case "ChangeCasingNames/Enabled": + language.ChangeCasingNames.Enabled = reader.Value; + break; + case "ChangeCasingNames/Name": + language.ChangeCasingNames.Name = reader.Value; + break; + case "ChangeCasingNames/LinesFoundX": + language.ChangeCasingNames.LinesFoundX = reader.Value; + break; + case "ChangeFrameRate/Title": + language.ChangeFrameRate.Title = reader.Value; + break; + case "ChangeFrameRate/ConvertFrameRateOfSubtitle": + language.ChangeFrameRate.ConvertFrameRateOfSubtitle = reader.Value; + break; + case "ChangeFrameRate/FromFrameRate": + language.ChangeFrameRate.FromFrameRate = reader.Value; + break; + case "ChangeFrameRate/ToFrameRate": + language.ChangeFrameRate.ToFrameRate = reader.Value; + break; + case "ChangeFrameRate/FrameRateNotCorrect": + language.ChangeFrameRate.FrameRateNotCorrect = reader.Value; + break; + case "ChangeFrameRate/FrameRateNotChanged": + language.ChangeFrameRate.FrameRateNotChanged = reader.Value; + break; + case "ChangeSpeedInPercent/Title": + language.ChangeSpeedInPercent.Title = reader.Value; + break; + case "ChangeSpeedInPercent/Info": + language.ChangeSpeedInPercent.Info = reader.Value; + break; + case "ChangeSpeedInPercent/Custom": + language.ChangeSpeedInPercent.Custom = reader.Value; + break; + case "ChangeSpeedInPercent/ToDropFrame": + language.ChangeSpeedInPercent.ToDropFrame = reader.Value; + break; + case "ChangeSpeedInPercent/FromDropFrame": + language.ChangeSpeedInPercent.FromDropFrame = reader.Value; + break; + case "CheckForUpdates/Title": + language.CheckForUpdates.Title = reader.Value; + break; + case "CheckForUpdates/CheckingForUpdates": + language.CheckForUpdates.CheckingForUpdates = reader.Value; + break; + case "CheckForUpdates/CheckingForUpdatesFailedX": + language.CheckForUpdates.CheckingForUpdatesFailedX = reader.Value; + break; + case "CheckForUpdates/CheckingForUpdatesNoneAvailable": + language.CheckForUpdates.CheckingForUpdatesNoneAvailable = reader.Value; + break; + case "CheckForUpdates/CheckingForUpdatesNewVersion": + language.CheckForUpdates.CheckingForUpdatesNewVersion = reader.Value; + break; + case "CheckForUpdates/InstallUpdate": + language.CheckForUpdates.InstallUpdate = reader.Value; + break; + case "CheckForUpdates/NoUpdates": + language.CheckForUpdates.NoUpdates = reader.Value; + break; + case "ChooseAudioTrack/Title": + language.ChooseAudioTrack.Title = reader.Value; + break; + case "ChooseEncoding/Title": + language.ChooseEncoding.Title = reader.Value; + break; + case "ChooseEncoding/CodePage": + language.ChooseEncoding.CodePage = reader.Value; + break; + case "ChooseEncoding/DisplayName": + language.ChooseEncoding.DisplayName = reader.Value; + break; + case "ChooseEncoding/PleaseSelectAnEncoding": + language.ChooseEncoding.PleaseSelectAnEncoding = reader.Value; + break; + case "ChooseLanguage/Title": + language.ChooseLanguage.Title = reader.Value; + break; + case "ChooseLanguage/Language": + language.ChooseLanguage.Language = reader.Value; + break; + case "ColorChooser/Title": + language.ColorChooser.Title = reader.Value; + break; + case "ColorChooser/Red": + language.ColorChooser.Red = reader.Value; + break; + case "ColorChooser/Green": + language.ColorChooser.Green = reader.Value; + break; + case "ColorChooser/Blue": + language.ColorChooser.Blue = reader.Value; + break; + case "ColorChooser/Alpha": + language.ColorChooser.Alpha = reader.Value; + break; + case "ColumnPaste/Title": + language.ColumnPaste.Title = reader.Value; + break; + case "ColumnPaste/ChooseColumn": + language.ColumnPaste.ChooseColumn = reader.Value; + break; + case "ColumnPaste/OverwriteShiftCellsDown": + language.ColumnPaste.OverwriteShiftCellsDown = reader.Value; + break; + case "ColumnPaste/Overwrite": + language.ColumnPaste.Overwrite = reader.Value; + break; + case "ColumnPaste/ShiftCellsDown": + language.ColumnPaste.ShiftCellsDown = reader.Value; + break; + case "ColumnPaste/TimeCodesOnly": + language.ColumnPaste.TimeCodesOnly = reader.Value; + break; + case "ColumnPaste/TextOnly": + language.ColumnPaste.TextOnly = reader.Value; + break; + case "ColumnPaste/OriginalTextOnly": + language.ColumnPaste.OriginalTextOnly = reader.Value; + break; + case "CompareSubtitles/Title": + language.CompareSubtitles.Title = reader.Value; + break; + case "CompareSubtitles/PreviousDifference": + language.CompareSubtitles.PreviousDifference = reader.Value; + break; + case "CompareSubtitles/NextDifference": + language.CompareSubtitles.NextDifference = reader.Value; + break; + case "CompareSubtitles/SubtitlesNotAlike": + language.CompareSubtitles.SubtitlesNotAlike = reader.Value; + break; + case "CompareSubtitles/XNumberOfDifference": + language.CompareSubtitles.XNumberOfDifference = reader.Value; + break; + case "CompareSubtitles/XNumberOfDifferenceAndPercentChanged": + language.CompareSubtitles.XNumberOfDifferenceAndPercentChanged = reader.Value; + break; + case "CompareSubtitles/XNumberOfDifferenceAndPercentLettersChanged": + language.CompareSubtitles.XNumberOfDifferenceAndPercentLettersChanged = reader.Value; + break; + case "CompareSubtitles/ShowOnlyDifferences": + language.CompareSubtitles.ShowOnlyDifferences = reader.Value; + break; + case "CompareSubtitles/IgnoreLineBreaks": + language.CompareSubtitles.IgnoreLineBreaks = reader.Value; + break; + case "CompareSubtitles/OnlyLookForDifferencesInText": + language.CompareSubtitles.OnlyLookForDifferencesInText = reader.Value; + break; + case "CompareSubtitles/CannotCompareWithImageBasedSubtitles": + language.CompareSubtitles.CannotCompareWithImageBasedSubtitles = reader.Value; + break; + case "DCinemaProperties/Title": + language.DCinemaProperties.Title = reader.Value; + break; + case "DCinemaProperties/TitleSmpte": + language.DCinemaProperties.TitleSmpte = reader.Value; + break; + case "DCinemaProperties/SubtitleId": + language.DCinemaProperties.SubtitleId = reader.Value; + break; + case "DCinemaProperties/GenerateId": + language.DCinemaProperties.GenerateId = reader.Value; + break; + case "DCinemaProperties/MovieTitle": + language.DCinemaProperties.MovieTitle = reader.Value; + break; + case "DCinemaProperties/ReelNumber": + language.DCinemaProperties.ReelNumber = reader.Value; + break; + case "DCinemaProperties/Language": + language.DCinemaProperties.Language = reader.Value; + break; + case "DCinemaProperties/IssueDate": + language.DCinemaProperties.IssueDate = reader.Value; + break; + case "DCinemaProperties/EditRate": + language.DCinemaProperties.EditRate = reader.Value; + break; + case "DCinemaProperties/TimeCodeRate": + language.DCinemaProperties.TimeCodeRate = reader.Value; + break; + case "DCinemaProperties/StartTime": + language.DCinemaProperties.StartTime = reader.Value; + break; + case "DCinemaProperties/Font": + language.DCinemaProperties.Font = reader.Value; + break; + case "DCinemaProperties/FontId": + language.DCinemaProperties.FontId = reader.Value; + break; + case "DCinemaProperties/FontUri": + language.DCinemaProperties.FontUri = reader.Value; + break; + case "DCinemaProperties/FontColor": + language.DCinemaProperties.FontColor = reader.Value; + break; + case "DCinemaProperties/FontEffect": + language.DCinemaProperties.FontEffect = reader.Value; + break; + case "DCinemaProperties/FontEffectColor": + language.DCinemaProperties.FontEffectColor = reader.Value; + break; + case "DCinemaProperties/FontSize": + language.DCinemaProperties.FontSize = reader.Value; + break; + case "DCinemaProperties/TopBottomMargin": + language.DCinemaProperties.TopBottomMargin = reader.Value; + break; + case "DCinemaProperties/FadeUpTime": + language.DCinemaProperties.FadeUpTime = reader.Value; + break; + case "DCinemaProperties/FadeDownTime": + language.DCinemaProperties.FadeDownTime = reader.Value; + break; + case "DCinemaProperties/ZPosition": + language.DCinemaProperties.ZPosition = reader.Value; + break; + case "DCinemaProperties/ZPositionHelp": + language.DCinemaProperties.ZPositionHelp = reader.Value; + break; + case "DCinemaProperties/ChooseColor": + language.DCinemaProperties.ChooseColor = reader.Value; + break; + case "DCinemaProperties/Generate": + language.DCinemaProperties.Generate = reader.Value; + break; + case "DurationsBridgeGaps/Title": + language.DurationsBridgeGaps.Title = reader.Value; + break; + case "DurationsBridgeGaps/GapsBridgedX": + language.DurationsBridgeGaps.GapsBridgedX = reader.Value; + break; + case "DurationsBridgeGaps/GapToNext": + language.DurationsBridgeGaps.GapToNext = reader.Value; + break; + case "DurationsBridgeGaps/BridgeGapsSmallerThanXPart1": + language.DurationsBridgeGaps.BridgeGapsSmallerThanXPart1 = reader.Value; + break; + case "DurationsBridgeGaps/BridgeGapsSmallerThanXPart2": + language.DurationsBridgeGaps.BridgeGapsSmallerThanXPart2 = reader.Value; + break; + case "DurationsBridgeGaps/MinMillisecondsBetweenLines": + language.DurationsBridgeGaps.MinMillisecondsBetweenLines = reader.Value; + break; + case "DurationsBridgeGaps/ProlongEndTime": + language.DurationsBridgeGaps.ProlongEndTime = reader.Value; + break; + case "DurationsBridgeGaps/DivideEven": + language.DurationsBridgeGaps.DivideEven = reader.Value; + break; + case "DvdSubRip/Title": + language.DvdSubRip.Title = reader.Value; + break; + case "DvdSubRip/DvdGroupTitle": + language.DvdSubRip.DvdGroupTitle = reader.Value; + break; + case "DvdSubRip/IfoFile": + language.DvdSubRip.IfoFile = reader.Value; + break; + case "DvdSubRip/IfoFiles": + language.DvdSubRip.IfoFiles = reader.Value; + break; + case "DvdSubRip/VobFiles": + language.DvdSubRip.VobFiles = reader.Value; + break; + case "DvdSubRip/Add": + language.DvdSubRip.Add = reader.Value; + break; + case "DvdSubRip/Remove": + language.DvdSubRip.Remove = reader.Value; + break; + case "DvdSubRip/Clear": + language.DvdSubRip.Clear = reader.Value; + break; + case "DvdSubRip/MoveUp": + language.DvdSubRip.MoveUp = reader.Value; + break; + case "DvdSubRip/MoveDown": + language.DvdSubRip.MoveDown = reader.Value; + break; + case "DvdSubRip/Languages": + language.DvdSubRip.Languages = reader.Value; + break; + case "DvdSubRip/PalNtsc": + language.DvdSubRip.PalNtsc = reader.Value; + break; + case "DvdSubRip/Pal": + language.DvdSubRip.Pal = reader.Value; + break; + case "DvdSubRip/Ntsc": + language.DvdSubRip.Ntsc = reader.Value; + break; + case "DvdSubRip/StartRipping": + language.DvdSubRip.StartRipping = reader.Value; + break; + case "DvdSubRip/Abort": + language.DvdSubRip.Abort = reader.Value; + break; + case "DvdSubRip/AbortedByUser": + language.DvdSubRip.AbortedByUser = reader.Value; + break; + case "DvdSubRip/ReadingSubtitleData": + language.DvdSubRip.ReadingSubtitleData = reader.Value; + break; + case "DvdSubRip/RippingVobFileXofYZ": + language.DvdSubRip.RippingVobFileXofYZ = reader.Value; + break; + case "DvdSubRip/WrongIfoType": + language.DvdSubRip.WrongIfoType = reader.Value; + break; + case "DvdSubRipChooseLanguage/Title": + language.DvdSubRipChooseLanguage.Title = reader.Value; + break; + case "DvdSubRipChooseLanguage/ChooseLanguageStreamId": + language.DvdSubRipChooseLanguage.ChooseLanguageStreamId = reader.Value; + break; + case "DvdSubRipChooseLanguage/UnknownLanguage": + language.DvdSubRipChooseLanguage.UnknownLanguage = reader.Value; + break; + case "DvdSubRipChooseLanguage/SubtitleImageXofYAndWidthXHeight": + language.DvdSubRipChooseLanguage.SubtitleImageXofYAndWidthXHeight = reader.Value; + break; + case "DvdSubRipChooseLanguage/SubtitleImage": + language.DvdSubRipChooseLanguage.SubtitleImage = reader.Value; + break; + case "EbuSaveOptions/Title": + language.EbuSaveOptions.Title = reader.Value; + break; + case "EbuSaveOptions/GeneralSubtitleInformation": + language.EbuSaveOptions.GeneralSubtitleInformation = reader.Value; + break; + case "EbuSaveOptions/CodePageNumber": + language.EbuSaveOptions.CodePageNumber = reader.Value; + break; + case "EbuSaveOptions/DiskFormatCode": + language.EbuSaveOptions.DiskFormatCode = reader.Value; + break; + case "EbuSaveOptions/DisplayStandardCode": + language.EbuSaveOptions.DisplayStandardCode = reader.Value; + break; + case "EbuSaveOptions/CharacterCodeTable": + language.EbuSaveOptions.CharacterCodeTable = reader.Value; + break; + case "EbuSaveOptions/LanguageCode": + language.EbuSaveOptions.LanguageCode = reader.Value; + break; + case "EbuSaveOptions/OriginalProgramTitle": + language.EbuSaveOptions.OriginalProgramTitle = reader.Value; + break; + case "EbuSaveOptions/OriginalEpisodeTitle": + language.EbuSaveOptions.OriginalEpisodeTitle = reader.Value; + break; + case "EbuSaveOptions/TranslatedProgramTitle": + language.EbuSaveOptions.TranslatedProgramTitle = reader.Value; + break; + case "EbuSaveOptions/TranslatedEpisodeTitle": + language.EbuSaveOptions.TranslatedEpisodeTitle = reader.Value; + break; + case "EbuSaveOptions/TranslatorsName": + language.EbuSaveOptions.TranslatorsName = reader.Value; + break; + case "EbuSaveOptions/SubtitleListReferenceCode": + language.EbuSaveOptions.SubtitleListReferenceCode = reader.Value; + break; + case "EbuSaveOptions/CountryOfOrigin": + language.EbuSaveOptions.CountryOfOrigin = reader.Value; + break; + case "EbuSaveOptions/TimeCodeStatus": + language.EbuSaveOptions.TimeCodeStatus = reader.Value; + break; + case "EbuSaveOptions/TimeCodeStartOfProgramme": + language.EbuSaveOptions.TimeCodeStartOfProgramme = reader.Value; + break; + case "EbuSaveOptions/RevisionNumber": + language.EbuSaveOptions.RevisionNumber = reader.Value; + break; + case "EbuSaveOptions/MaxNoOfDisplayableChars": + language.EbuSaveOptions.MaxNoOfDisplayableChars = reader.Value; + break; + case "EbuSaveOptions/MaxNumberOfDisplayableRows": + language.EbuSaveOptions.MaxNumberOfDisplayableRows = reader.Value; + break; + case "EbuSaveOptions/DiskSequenceNumber": + language.EbuSaveOptions.DiskSequenceNumber = reader.Value; + break; + case "EbuSaveOptions/TotalNumberOfDisks": + language.EbuSaveOptions.TotalNumberOfDisks = reader.Value; + break; + case "EbuSaveOptions/Import": + language.EbuSaveOptions.Import = reader.Value; + break; + case "EbuSaveOptions/TextAndTimingInformation": + language.EbuSaveOptions.TextAndTimingInformation = reader.Value; + break; + case "EbuSaveOptions/JustificationCode": + language.EbuSaveOptions.JustificationCode = reader.Value; + break; + case "EbuSaveOptions/Errors": + language.EbuSaveOptions.Errors = reader.Value; + break; + case "EbuSaveOptions/ErrorsX": + language.EbuSaveOptions.ErrorsX = reader.Value; + break; + case "EbuSaveOptions/MaxLengthError": + language.EbuSaveOptions.MaxLengthError = reader.Value; + break; + case "EbuSaveOptions/TextUnchangedPresentation": + language.EbuSaveOptions.TextUnchangedPresentation = reader.Value; + break; + case "EbuSaveOptions/TextLeftJustifiedText": + language.EbuSaveOptions.TextLeftJustifiedText = reader.Value; + break; + case "EbuSaveOptions/TextCenteredText": + language.EbuSaveOptions.TextCenteredText = reader.Value; + break; + case "EbuSaveOptions/TextRightJustifiedText": + language.EbuSaveOptions.TextRightJustifiedText = reader.Value; + break; + case "EffectKaraoke/Title": + language.EffectKaraoke.Title = reader.Value; + break; + case "EffectKaraoke/ChooseColor": + language.EffectKaraoke.ChooseColor = reader.Value; + break; + case "EffectKaraoke/TotalMilliseconds": + language.EffectKaraoke.TotalMilliseconds = reader.Value; + break; + case "EffectKaraoke/EndDelayInMilliseconds": + language.EffectKaraoke.EndDelayInMilliseconds = reader.Value; + break; + case "EffectTypewriter/Title": + language.EffectTypewriter.Title = reader.Value; + break; + case "EffectTypewriter/TotalMilliseconds": + language.EffectTypewriter.TotalMilliseconds = reader.Value; + break; + case "EffectTypewriter/EndDelayInMilliseconds": + language.EffectTypewriter.EndDelayInMilliseconds = reader.Value; + break; + case "ExportCustomText/Title": + language.ExportCustomText.Title = reader.Value; + break; + case "ExportCustomText/Formats": + language.ExportCustomText.Formats = reader.Value; + break; + case "ExportCustomText/New": + language.ExportCustomText.New = reader.Value; + break; + case "ExportCustomText/Edit": + language.ExportCustomText.Edit = reader.Value; + break; + case "ExportCustomText/Delete": + language.ExportCustomText.Delete = reader.Value; + break; + case "ExportCustomText/SaveAs": + language.ExportCustomText.SaveAs = reader.Value; + break; + case "ExportCustomText/SaveSubtitleAs": + language.ExportCustomText.SaveSubtitleAs = reader.Value; + break; + case "ExportCustomText/SubtitleExportedInCustomFormatToX": + language.ExportCustomText.SubtitleExportedInCustomFormatToX = reader.Value; + break; + case "ExportCustomTextFormat/Title": + language.ExportCustomTextFormat.Title = reader.Value; + break; + case "ExportCustomTextFormat/Template": + language.ExportCustomTextFormat.Template = reader.Value; + break; + case "ExportCustomTextFormat/Header": + language.ExportCustomTextFormat.Header = reader.Value; + break; + case "ExportCustomTextFormat/TextLine": + language.ExportCustomTextFormat.TextLine = reader.Value; + break; + case "ExportCustomTextFormat/TimeCode": + language.ExportCustomTextFormat.TimeCode = reader.Value; + break; + case "ExportCustomTextFormat/NewLine": + language.ExportCustomTextFormat.NewLine = reader.Value; + break; + case "ExportCustomTextFormat/Footer": + language.ExportCustomTextFormat.Footer = reader.Value; + break; + case "ExportCustomTextFormat/DoNotModify": + language.ExportCustomTextFormat.DoNotModify = reader.Value; + break; + case "ExportPngXml/Title": + language.ExportPngXml.Title = reader.Value; + break; + case "ExportPngXml/ImageSettings": + language.ExportPngXml.ImageSettings = reader.Value; + break; + case "ExportPngXml/FontFamily": + language.ExportPngXml.FontFamily = reader.Value; + break; + case "ExportPngXml/FontSize": + language.ExportPngXml.FontSize = reader.Value; + break; + case "ExportPngXml/FontColor": + language.ExportPngXml.FontColor = reader.Value; + break; + case "ExportPngXml/BorderColor": + language.ExportPngXml.BorderColor = reader.Value; + break; + case "ExportPngXml/BorderWidth": + language.ExportPngXml.BorderWidth = reader.Value; + break; + case "ExportPngXml/BorderStyle": + language.ExportPngXml.BorderStyle = reader.Value; + break; + case "ExportPngXml/BorderStyleOneBox": + language.ExportPngXml.BorderStyleOneBox = reader.Value; + break; + case "ExportPngXml/BorderStyleBoxForEachLine": + language.ExportPngXml.BorderStyleBoxForEachLine = reader.Value; + break; + case "ExportPngXml/BorderStyleNormalWidthX": + language.ExportPngXml.BorderStyleNormalWidthX = reader.Value; + break; + case "ExportPngXml/ShadowColor": + language.ExportPngXml.ShadowColor = reader.Value; + break; + case "ExportPngXml/ShadowWidth": + language.ExportPngXml.ShadowWidth = reader.Value; + break; + case "ExportPngXml/Transparency": + language.ExportPngXml.Transparency = reader.Value; + break; + case "ExportPngXml/ImageFormat": + language.ExportPngXml.ImageFormat = reader.Value; + break; + case "ExportPngXml/FullFrameImage": + language.ExportPngXml.FullFrameImage = reader.Value; + break; + case "ExportPngXml/SimpleRendering": + language.ExportPngXml.SimpleRendering = reader.Value; + break; + case "ExportPngXml/AntiAliasingWithTransparency": + language.ExportPngXml.AntiAliasingWithTransparency = reader.Value; + break; + case "ExportPngXml/Text3D": + language.ExportPngXml.Text3D = reader.Value; + break; + case "ExportPngXml/SideBySide3D": + language.ExportPngXml.SideBySide3D = reader.Value; + break; + case "ExportPngXml/HalfTopBottom3D": + language.ExportPngXml.HalfTopBottom3D = reader.Value; + break; + case "ExportPngXml/Depth": + language.ExportPngXml.Depth = reader.Value; + break; + case "ExportPngXml/ExportAllLines": + language.ExportPngXml.ExportAllLines = reader.Value; + break; + case "ExportPngXml/XImagesSavedInY": + language.ExportPngXml.XImagesSavedInY = reader.Value; + break; + case "ExportPngXml/VideoResolution": + language.ExportPngXml.VideoResolution = reader.Value; + break; + case "ExportPngXml/Align": + language.ExportPngXml.Align = reader.Value; + break; + case "ExportPngXml/Left": + language.ExportPngXml.Left = reader.Value; + break; + case "ExportPngXml/Right": + language.ExportPngXml.Right = reader.Value; + break; + case "ExportPngXml/Center": + language.ExportPngXml.Center = reader.Value; + break; + case "ExportPngXml/BottomMargin": + language.ExportPngXml.BottomMargin = reader.Value; + break; + case "ExportPngXml/LeftRightMargin": + language.ExportPngXml.LeftRightMargin = reader.Value; + break; + case "ExportPngXml/SaveBluRraySupAs": + language.ExportPngXml.SaveBluRraySupAs = reader.Value; + break; + case "ExportPngXml/SaveVobSubAs": + language.ExportPngXml.SaveVobSubAs = reader.Value; + break; + case "ExportPngXml/SaveFabImageScriptAs": + language.ExportPngXml.SaveFabImageScriptAs = reader.Value; + break; + case "ExportPngXml/SaveDvdStudioProStlAs": + language.ExportPngXml.SaveDvdStudioProStlAs = reader.Value; + break; + case "ExportPngXml/SaveDigitalCinemaInteropAs": + language.ExportPngXml.SaveDigitalCinemaInteropAs = reader.Value; + break; + case "ExportPngXml/SavePremiereEdlAs": + language.ExportPngXml.SavePremiereEdlAs = reader.Value; + break; + case "ExportPngXml/SaveFcpAs": + language.ExportPngXml.SaveFcpAs = reader.Value; + break; + case "ExportPngXml/SaveDostAs": + language.ExportPngXml.SaveDostAs = reader.Value; + break; + case "ExportPngXml/SomeLinesWereTooLongX": + language.ExportPngXml.SomeLinesWereTooLongX = reader.Value; + break; + case "ExportPngXml/LineHeight": + language.ExportPngXml.LineHeight = reader.Value; + break; + case "ExportPngXml/BoxSingleLine": + language.ExportPngXml.BoxSingleLine = reader.Value; + break; + case "ExportPngXml/BoxMultiLine": + language.ExportPngXml.BoxMultiLine = reader.Value; + break; + case "ExportPngXml/Forced": + language.ExportPngXml.Forced = reader.Value; + break; + case "ExportPngXml/ChooseBackgroundColor": + language.ExportPngXml.ChooseBackgroundColor = reader.Value; + break; + case "ExportPngXml/SaveImageAs": + language.ExportPngXml.SaveImageAs = reader.Value; + break; + case "ExportText/Title": + language.ExportText.Title = reader.Value; + break; + case "ExportText/Preview": + language.ExportText.Preview = reader.Value; + break; + case "ExportText/ExportOptions": + language.ExportText.ExportOptions = reader.Value; + break; + case "ExportText/FormatText": + language.ExportText.FormatText = reader.Value; + break; + case "ExportText/None": + language.ExportText.None = reader.Value; + break; + case "ExportText/MergeAllLines": + language.ExportText.MergeAllLines = reader.Value; + break; + case "ExportText/UnbreakLines": + language.ExportText.UnbreakLines = reader.Value; + break; + case "ExportText/RemoveStyling": + language.ExportText.RemoveStyling = reader.Value; + break; + case "ExportText/ShowLineNumbers": + language.ExportText.ShowLineNumbers = reader.Value; + break; + case "ExportText/AddNewLineAfterLineNumber": + language.ExportText.AddNewLineAfterLineNumber = reader.Value; + break; + case "ExportText/ShowTimeCode": + language.ExportText.ShowTimeCode = reader.Value; + break; + case "ExportText/AddNewLineAfterTimeCode": + language.ExportText.AddNewLineAfterTimeCode = reader.Value; + break; + case "ExportText/AddNewLineAfterTexts": + language.ExportText.AddNewLineAfterTexts = reader.Value; + break; + case "ExportText/AddNewLineBetweenSubtitles": + language.ExportText.AddNewLineBetweenSubtitles = reader.Value; + break; + case "ExportText/TimeCodeFormat": + language.ExportText.TimeCodeFormat = reader.Value; + break; + case "ExportText/Srt": + language.ExportText.Srt = reader.Value; + break; + case "ExportText/Milliseconds": + language.ExportText.Milliseconds = reader.Value; + break; + case "ExportText/HHMMSSFF": + language.ExportText.HHMMSSFF = reader.Value; + break; + case "ExportText/TimeCodeSeparator": + language.ExportText.TimeCodeSeparator = reader.Value; + break; + case "ExtractDateTimeInfo/Title": + language.ExtractDateTimeInfo.Title = reader.Value; + break; + case "ExtractDateTimeInfo/OpenVideoFile": + language.ExtractDateTimeInfo.OpenVideoFile = reader.Value; + break; + case "ExtractDateTimeInfo/StartFrom": + language.ExtractDateTimeInfo.StartFrom = reader.Value; + break; + case "ExtractDateTimeInfo/DateTimeFormat": + language.ExtractDateTimeInfo.DateTimeFormat = reader.Value; + break; + case "ExtractDateTimeInfo/Example": + language.ExtractDateTimeInfo.Example = reader.Value; + break; + case "ExtractDateTimeInfo/GenerateSubtitle": + language.ExtractDateTimeInfo.GenerateSubtitle = reader.Value; + break; + case "FindDialog/Title": + language.FindDialog.Title = reader.Value; + break; + case "FindDialog/Find": + language.FindDialog.Find = reader.Value; + break; + case "FindDialog/Normal": + language.FindDialog.Normal = reader.Value; + break; + case "FindDialog/CaseSensitive": + language.FindDialog.CaseSensitive = reader.Value; + break; + case "FindDialog/RegularExpression": + language.FindDialog.RegularExpression = reader.Value; + break; + case "FindSubtitleLine/Title": + language.FindSubtitleLine.Title = reader.Value; + break; + case "FindSubtitleLine/Find": + language.FindSubtitleLine.Find = reader.Value; + break; + case "FindSubtitleLine/FindNext": + language.FindSubtitleLine.FindNext = reader.Value; + break; + case "FixCommonErrors/Title": + language.FixCommonErrors.Title = reader.Value; + break; + case "FixCommonErrors/Step1": + language.FixCommonErrors.Step1 = reader.Value; + break; + case "FixCommonErrors/WhatToFix": + language.FixCommonErrors.WhatToFix = reader.Value; + break; + case "FixCommonErrors/Example": + language.FixCommonErrors.Example = reader.Value; + break; + case "FixCommonErrors/SelectAll": + language.FixCommonErrors.SelectAll = reader.Value; + break; + case "FixCommonErrors/InverseSelection": + language.FixCommonErrors.InverseSelection = reader.Value; + break; + case "FixCommonErrors/Back": + language.FixCommonErrors.Back = reader.Value; + break; + case "FixCommonErrors/Next": + language.FixCommonErrors.Next = reader.Value; + break; + case "FixCommonErrors/Step2": + language.FixCommonErrors.Step2 = reader.Value; + break; + case "FixCommonErrors/Fixes": + language.FixCommonErrors.Fixes = reader.Value; + break; + case "FixCommonErrors/Log": + language.FixCommonErrors.Log = reader.Value; + break; + case "FixCommonErrors/Function": + language.FixCommonErrors.Function = reader.Value; + break; + case "FixCommonErrors/RemovedEmptyLine": + language.FixCommonErrors.RemovedEmptyLine = reader.Value; + break; + case "FixCommonErrors/RemovedEmptyLineAtTop": + language.FixCommonErrors.RemovedEmptyLineAtTop = reader.Value; + break; + case "FixCommonErrors/RemovedEmptyLineAtBottom": + language.FixCommonErrors.RemovedEmptyLineAtBottom = reader.Value; + break; + case "FixCommonErrors/RemovedEmptyLinesUnsedLineBreaks": + language.FixCommonErrors.RemovedEmptyLinesUnsedLineBreaks = reader.Value; + break; + case "FixCommonErrors/EmptyLinesRemovedX": + language.FixCommonErrors.EmptyLinesRemovedX = reader.Value; + break; + case "FixCommonErrors/FixOverlappingDisplayTimes": + language.FixCommonErrors.FixOverlappingDisplayTimes = reader.Value; + break; + case "FixCommonErrors/FixShortDisplayTimes": + language.FixCommonErrors.FixShortDisplayTimes = reader.Value; + break; + case "FixCommonErrors/FixLongDisplayTimes": + language.FixCommonErrors.FixLongDisplayTimes = reader.Value; + break; + case "FixCommonErrors/FixInvalidItalicTags": + language.FixCommonErrors.FixInvalidItalicTags = reader.Value; + break; + case "FixCommonErrors/RemoveUnneededSpaces": + language.FixCommonErrors.RemoveUnneededSpaces = reader.Value; + break; + case "FixCommonErrors/RemoveUnneededPeriods": + language.FixCommonErrors.RemoveUnneededPeriods = reader.Value; + break; + case "FixCommonErrors/FixMissingSpaces": + language.FixCommonErrors.FixMissingSpaces = reader.Value; + break; + case "FixCommonErrors/BreakLongLines": + language.FixCommonErrors.BreakLongLines = reader.Value; + break; + case "FixCommonErrors/RemoveLineBreaks": + language.FixCommonErrors.RemoveLineBreaks = reader.Value; + break; + case "FixCommonErrors/RemoveLineBreaksAll": + language.FixCommonErrors.RemoveLineBreaksAll = reader.Value; + break; + case "FixCommonErrors/FixUppercaseIInsindeLowercaseWords": + language.FixCommonErrors.FixUppercaseIInsindeLowercaseWords = reader.Value; + break; + case "FixCommonErrors/FixDoubleApostrophes": + language.FixCommonErrors.FixDoubleApostrophes = reader.Value; + break; + case "FixCommonErrors/AddPeriods": + language.FixCommonErrors.AddPeriods = reader.Value; + break; + case "FixCommonErrors/StartWithUppercaseLetterAfterParagraph": + language.FixCommonErrors.StartWithUppercaseLetterAfterParagraph = reader.Value; + break; + case "FixCommonErrors/StartWithUppercaseLetterAfterPeriodInsideParagraph": + language.FixCommonErrors.StartWithUppercaseLetterAfterPeriodInsideParagraph = reader.Value; + break; + case "FixCommonErrors/StartWithUppercaseLetterAfterColon": + language.FixCommonErrors.StartWithUppercaseLetterAfterColon = reader.Value; + break; + case "FixCommonErrors/FixLowercaseIToUppercaseI": + language.FixCommonErrors.FixLowercaseIToUppercaseI = reader.Value; + break; + case "FixCommonErrors/FixCommonOcrErrors": + language.FixCommonErrors.FixCommonOcrErrors = reader.Value; + break; + case "FixCommonErrors/CommonOcrErrorsFixed": + language.FixCommonErrors.CommonOcrErrorsFixed = reader.Value; + break; + case "FixCommonErrors/RemoveSpaceBetweenNumber": + language.FixCommonErrors.RemoveSpaceBetweenNumber = reader.Value; + break; + case "FixCommonErrors/FixDialogsOnOneLine": + language.FixCommonErrors.FixDialogsOnOneLine = reader.Value; + break; + case "FixCommonErrors/RemoveSpaceBetweenNumbersFixed": + language.FixCommonErrors.RemoveSpaceBetweenNumbersFixed = reader.Value; + break; + case "FixCommonErrors/FixTurkishAnsi": + language.FixCommonErrors.FixTurkishAnsi = reader.Value; + break; + case "FixCommonErrors/FixDanishLetterI": + language.FixCommonErrors.FixDanishLetterI = reader.Value; + break; + case "FixCommonErrors/FixSpanishInvertedQuestionAndExclamationMarks": + language.FixCommonErrors.FixSpanishInvertedQuestionAndExclamationMarks = reader.Value; + break; + case "FixCommonErrors/AddMissingQuote": + language.FixCommonErrors.AddMissingQuote = reader.Value; + break; + case "FixCommonErrors/AddMissingQuotes": + language.FixCommonErrors.AddMissingQuotes = reader.Value; + break; + case "FixCommonErrors/FixHyphens": + language.FixCommonErrors.FixHyphens = reader.Value; + break; + case "FixCommonErrors/FixHyphensAdd": + language.FixCommonErrors.FixHyphensAdd = reader.Value; + break; + case "FixCommonErrors/FixHyphen": + language.FixCommonErrors.FixHyphen = reader.Value; + break; + case "FixCommonErrors/XHyphensFixed": + language.FixCommonErrors.XHyphensFixed = reader.Value; + break; + case "FixCommonErrors/AddMissingQuotesExample": + language.FixCommonErrors.AddMissingQuotesExample = reader.Value; + break; + case "FixCommonErrors/XMissingQuotesAdded": + language.FixCommonErrors.XMissingQuotesAdded = reader.Value; + break; + case "FixCommonErrors/Fix3PlusLines": + language.FixCommonErrors.Fix3PlusLines = reader.Value; + break; + case "FixCommonErrors/Fix3PlusLine": + language.FixCommonErrors.Fix3PlusLine = reader.Value; + break; + case "FixCommonErrors/X3PlusLinesFixed": + language.FixCommonErrors.X3PlusLinesFixed = reader.Value; + break; + case "FixCommonErrors/Analysing": + language.FixCommonErrors.Analysing = reader.Value; + break; + case "FixCommonErrors/NothingToFix": + language.FixCommonErrors.NothingToFix = reader.Value; + break; + case "FixCommonErrors/FixesFoundX": + language.FixCommonErrors.FixesFoundX = reader.Value; + break; + case "FixCommonErrors/XFixesApplied": + language.FixCommonErrors.XFixesApplied = reader.Value; + break; + case "FixCommonErrors/NothingToFixBut": + language.FixCommonErrors.NothingToFixBut = reader.Value; + break; + case "FixCommonErrors/FixLowercaseIToUppercaseICheckedButCurrentLanguageIsNotEnglish": + language.FixCommonErrors.FixLowercaseIToUppercaseICheckedButCurrentLanguageIsNotEnglish = reader.Value; + break; + case "FixCommonErrors/Continue": + language.FixCommonErrors.Continue = reader.Value; + break; + case "FixCommonErrors/ContinueAnyway": + language.FixCommonErrors.ContinueAnyway = reader.Value; + break; + case "FixCommonErrors/UncheckedFixLowercaseIToUppercaseI": + language.FixCommonErrors.UncheckedFixLowercaseIToUppercaseI = reader.Value; + break; + case "FixCommonErrors/XIsChangedToUppercase": + language.FixCommonErrors.XIsChangedToUppercase = reader.Value; + break; + case "FixCommonErrors/FixFirstLetterToUppercaseAfterParagraph": + language.FixCommonErrors.FixFirstLetterToUppercaseAfterParagraph = reader.Value; + break; + case "FixCommonErrors/MergeShortLine": + language.FixCommonErrors.MergeShortLine = reader.Value; + break; + case "FixCommonErrors/MergeShortLineAll": + language.FixCommonErrors.MergeShortLineAll = reader.Value; + break; + case "FixCommonErrors/XLineBreaksAdded": + language.FixCommonErrors.XLineBreaksAdded = reader.Value; + break; + case "FixCommonErrors/BreakLongLine": + language.FixCommonErrors.BreakLongLine = reader.Value; + break; + case "FixCommonErrors/FixLongDisplayTime": + language.FixCommonErrors.FixLongDisplayTime = reader.Value; + break; + case "FixCommonErrors/FixInvalidItalicTag": + language.FixCommonErrors.FixInvalidItalicTag = reader.Value; + break; + case "FixCommonErrors/FixShortDisplayTime": + language.FixCommonErrors.FixShortDisplayTime = reader.Value; + break; + case "FixCommonErrors/FixOverlappingDisplayTime": + language.FixCommonErrors.FixOverlappingDisplayTime = reader.Value; + break; + case "FixCommonErrors/FixInvalidItalicTagsExample": + language.FixCommonErrors.FixInvalidItalicTagsExample = reader.Value; + break; + case "FixCommonErrors/RemoveUnneededSpacesExample": + language.FixCommonErrors.RemoveUnneededSpacesExample = reader.Value; + break; + case "FixCommonErrors/RemoveUnneededPeriodsExample": + language.FixCommonErrors.RemoveUnneededPeriodsExample = reader.Value; + break; + case "FixCommonErrors/FixMissingSpacesExample": + language.FixCommonErrors.FixMissingSpacesExample = reader.Value; + break; + case "FixCommonErrors/FixUppercaseIInsindeLowercaseWordsExample": + language.FixCommonErrors.FixUppercaseIInsindeLowercaseWordsExample = reader.Value; + break; + case "FixCommonErrors/FixLowercaseIToUppercaseIExample": + language.FixCommonErrors.FixLowercaseIToUppercaseIExample = reader.Value; + break; + case "FixCommonErrors/StartTimeLaterThanEndTime": + language.FixCommonErrors.StartTimeLaterThanEndTime = reader.Value; + break; + case "FixCommonErrors/UnableToFixStartTimeLaterThanEndTime": + language.FixCommonErrors.UnableToFixStartTimeLaterThanEndTime = reader.Value; + break; + case "FixCommonErrors/XFixedToYZ": + language.FixCommonErrors.XFixedToYZ = reader.Value; + break; + case "FixCommonErrors/UnableToFixTextXY": + language.FixCommonErrors.UnableToFixTextXY = reader.Value; + break; + case "FixCommonErrors/XOverlappingTimestampsFixed": + language.FixCommonErrors.XOverlappingTimestampsFixed = reader.Value; + break; + case "FixCommonErrors/XDisplayTimesProlonged": + language.FixCommonErrors.XDisplayTimesProlonged = reader.Value; + break; + case "FixCommonErrors/XInvalidHtmlTagsFixed": + language.FixCommonErrors.XInvalidHtmlTagsFixed = reader.Value; + break; + case "FixCommonErrors/XDisplayTimesShortned": + language.FixCommonErrors.XDisplayTimesShortned = reader.Value; + break; + case "FixCommonErrors/XLinesUnbreaked": + language.FixCommonErrors.XLinesUnbreaked = reader.Value; + break; + case "FixCommonErrors/UnneededSpace": + language.FixCommonErrors.UnneededSpace = reader.Value; + break; + case "FixCommonErrors/XUnneededSpacesRemoved": + language.FixCommonErrors.XUnneededSpacesRemoved = reader.Value; + break; + case "FixCommonErrors/UnneededPeriod": + language.FixCommonErrors.UnneededPeriod = reader.Value; + break; + case "FixCommonErrors/XUnneededPeriodsRemoved": + language.FixCommonErrors.XUnneededPeriodsRemoved = reader.Value; + break; + case "FixCommonErrors/FixMissingSpace": + language.FixCommonErrors.FixMissingSpace = reader.Value; + break; + case "FixCommonErrors/XMissingSpacesAdded": + language.FixCommonErrors.XMissingSpacesAdded = reader.Value; + break; + case "FixCommonErrors/FixUppercaseIInsideLowercaseWord": + language.FixCommonErrors.FixUppercaseIInsideLowercaseWord = reader.Value; + break; + case "FixCommonErrors/XPeriodsAdded": + language.FixCommonErrors.XPeriodsAdded = reader.Value; + break; + case "FixCommonErrors/FixMissingPeriodAtEndOfLine": + language.FixCommonErrors.FixMissingPeriodAtEndOfLine = reader.Value; + break; + case "FixCommonErrors/XDoubleApostrophesFixed": + language.FixCommonErrors.XDoubleApostrophesFixed = reader.Value; + break; + case "FixCommonErrors/XUppercaseIsFoundInsideLowercaseWords": + language.FixCommonErrors.XUppercaseIsFoundInsideLowercaseWords = reader.Value; + break; + case "FixCommonErrors/RefreshFixes": + language.FixCommonErrors.RefreshFixes = reader.Value; + break; + case "FixCommonErrors/ApplyFixes": + language.FixCommonErrors.ApplyFixes = reader.Value; + break; + case "FixCommonErrors/AutoBreak": + language.FixCommonErrors.AutoBreak = reader.Value; + break; + case "FixCommonErrors/Unbreak": + language.FixCommonErrors.Unbreak = reader.Value; + break; + case "FixCommonErrors/FixDoubleDash": + language.FixCommonErrors.FixDoubleDash = reader.Value; + break; + case "FixCommonErrors/FixDoubleGreaterThan": + language.FixCommonErrors.FixDoubleGreaterThan = reader.Value; + break; + case "FixCommonErrors/FixEllipsesStart": + language.FixCommonErrors.FixEllipsesStart = reader.Value; + break; + case "FixCommonErrors/FixMissingOpenBracket": + language.FixCommonErrors.FixMissingOpenBracket = reader.Value; + break; + case "FixCommonErrors/FixMusicNotation": + language.FixCommonErrors.FixMusicNotation = reader.Value; + break; + case "FixCommonErrors/XFixDoubleDash": + language.FixCommonErrors.XFixDoubleDash = reader.Value; + break; + case "FixCommonErrors/XFixDoubleGreaterThan": + language.FixCommonErrors.XFixDoubleGreaterThan = reader.Value; + break; + case "FixCommonErrors/XFixEllipsesStart": + language.FixCommonErrors.XFixEllipsesStart = reader.Value; + break; + case "FixCommonErrors/XFixMissingOpenBracket": + language.FixCommonErrors.XFixMissingOpenBracket = reader.Value; + break; + case "FixCommonErrors/XFixMusicNotation": + language.FixCommonErrors.XFixMusicNotation = reader.Value; + break; + case "FixCommonErrors/FixDoubleDashExample": + language.FixCommonErrors.FixDoubleDashExample = reader.Value; + break; + case "FixCommonErrors/FixDoubleGreaterThanExample": + language.FixCommonErrors.FixDoubleGreaterThanExample = reader.Value; + break; + case "FixCommonErrors/FixEllipsesStartExample": + language.FixCommonErrors.FixEllipsesStartExample = reader.Value; + break; + case "FixCommonErrors/FixMissingOpenBracketExample": + language.FixCommonErrors.FixMissingOpenBracketExample = reader.Value; + break; + case "FixCommonErrors/FixMusicNotationExample": + language.FixCommonErrors.FixMusicNotationExample = reader.Value; + break; + case "FixCommonErrors/NumberOfImportantLogMessages": + language.FixCommonErrors.NumberOfImportantLogMessages = reader.Value; + break; + case "FixCommonErrors/FixedOkXY": + language.FixCommonErrors.FixedOkXY = reader.Value; + break; + case "FixCommonErrors/FixOcrErrorExample": + language.FixCommonErrors.FixOcrErrorExample = reader.Value; + break; + case "FixCommonErrors/FixSpaceBetweenNumbersExample": + language.FixCommonErrors.FixSpaceBetweenNumbersExample = reader.Value; + break; + case "FixCommonErrors/FixDialogsOneLineExample": + language.FixCommonErrors.FixDialogsOneLineExample = reader.Value; + break; + case "GetDictionaries/Title": + language.GetDictionaries.Title = reader.Value; + break; + case "GetDictionaries/DescriptionLine1": + language.GetDictionaries.DescriptionLine1 = reader.Value; + break; + case "GetDictionaries/DescriptionLine2": + language.GetDictionaries.DescriptionLine2 = reader.Value; + break; + case "GetDictionaries/GetDictionariesHere": + language.GetDictionaries.GetDictionariesHere = reader.Value; + break; + case "GetDictionaries/OpenOpenOfficeWiki": + language.GetDictionaries.OpenOpenOfficeWiki = reader.Value; + break; + case "GetDictionaries/GetAllDictionaries": + language.GetDictionaries.GetAllDictionaries = reader.Value; + break; + case "GetDictionaries/ChooseLanguageAndClickDownload": + language.GetDictionaries.ChooseLanguageAndClickDownload = reader.Value; + break; + case "GetDictionaries/OpenDictionariesFolder": + language.GetDictionaries.OpenDictionariesFolder = reader.Value; + break; + case "GetDictionaries/Download": + language.GetDictionaries.Download = reader.Value; + break; + case "GetDictionaries/XDownloaded": + language.GetDictionaries.XDownloaded = reader.Value; + break; + case "GetTesseractDictionaries/Title": + language.GetTesseractDictionaries.Title = reader.Value; + break; + case "GetTesseractDictionaries/DescriptionLine1": + language.GetTesseractDictionaries.DescriptionLine1 = reader.Value; + break; + case "GetTesseractDictionaries/DownloadFailed": + language.GetTesseractDictionaries.DownloadFailed = reader.Value; + break; + case "GetTesseractDictionaries/GetDictionariesHere": + language.GetTesseractDictionaries.GetDictionariesHere = reader.Value; + break; + case "GetTesseractDictionaries/OpenOpenOfficeWiki": + language.GetTesseractDictionaries.OpenOpenOfficeWiki = reader.Value; + break; + case "GetTesseractDictionaries/GetAllDictionaries": + language.GetTesseractDictionaries.GetAllDictionaries = reader.Value; + break; + case "GetTesseractDictionaries/ChooseLanguageAndClickDownload": + language.GetTesseractDictionaries.ChooseLanguageAndClickDownload = reader.Value; + break; + case "GetTesseractDictionaries/OpenDictionariesFolder": + language.GetTesseractDictionaries.OpenDictionariesFolder = reader.Value; + break; + case "GetTesseractDictionaries/Download": + language.GetTesseractDictionaries.Download = reader.Value; + break; + case "GetTesseractDictionaries/XDownloaded": + language.GetTesseractDictionaries.XDownloaded = reader.Value; + break; + case "GoogleTranslate/Title": + language.GoogleTranslate.Title = reader.Value; + break; + case "GoogleTranslate/From": + language.GoogleTranslate.From = reader.Value; + break; + case "GoogleTranslate/To": + language.GoogleTranslate.To = reader.Value; + break; + case "GoogleTranslate/Translate": + language.GoogleTranslate.Translate = reader.Value; + break; + case "GoogleTranslate/PleaseWait": + language.GoogleTranslate.PleaseWait = reader.Value; + break; + case "GoogleTranslate/PoweredByGoogleTranslate": + language.GoogleTranslate.PoweredByGoogleTranslate = reader.Value; + break; + case "GoogleTranslate/PoweredByMicrosoftTranslate": + language.GoogleTranslate.PoweredByMicrosoftTranslate = reader.Value; + break; + case "GoogleOrMicrosoftTranslate/Title": + language.GoogleOrMicrosoftTranslate.Title = reader.Value; + break; + case "GoogleOrMicrosoftTranslate/From": + language.GoogleOrMicrosoftTranslate.From = reader.Value; + break; + case "GoogleOrMicrosoftTranslate/To": + language.GoogleOrMicrosoftTranslate.To = reader.Value; + break; + case "GoogleOrMicrosoftTranslate/Translate": + language.GoogleOrMicrosoftTranslate.Translate = reader.Value; + break; + case "GoogleOrMicrosoftTranslate/SourceText": + language.GoogleOrMicrosoftTranslate.SourceText = reader.Value; + break; + case "GoogleOrMicrosoftTranslate/GoogleTranslate": + language.GoogleOrMicrosoftTranslate.GoogleTranslate = reader.Value; + break; + case "GoogleOrMicrosoftTranslate/MicrosoftTranslate": + language.GoogleOrMicrosoftTranslate.MicrosoftTranslate = reader.Value; + break; + case "GoToLine/Title": + language.GoToLine.Title = reader.Value; + break; + case "GoToLine/XIsNotAValidNumber": + language.GoToLine.XIsNotAValidNumber = reader.Value; + break; + case "ImportImages/Title": + language.ImportImages.Title = reader.Value; + break; + case "ImportImages/ImageFiles": + language.ImportImages.ImageFiles = reader.Value; + break; + case "ImportImages/Input": + language.ImportImages.Input = reader.Value; + break; + case "ImportImages/InputDescription": + language.ImportImages.InputDescription = reader.Value; + break; + case "ImportSceneChanges/Title": + language.ImportSceneChanges.Title = reader.Value; + break; + case "ImportSceneChanges/OpenTextFile": + language.ImportSceneChanges.OpenTextFile = reader.Value; + break; + case "ImportSceneChanges/ImportOptions": + language.ImportSceneChanges.ImportOptions = reader.Value; + break; + case "ImportSceneChanges/TextFiles": + language.ImportSceneChanges.TextFiles = reader.Value; + break; + case "ImportSceneChanges/TimeCodes": + language.ImportSceneChanges.TimeCodes = reader.Value; + break; + case "ImportSceneChanges/Frames": + language.ImportSceneChanges.Frames = reader.Value; + break; + case "ImportSceneChanges/Seconds": + language.ImportSceneChanges.Seconds = reader.Value; + break; + case "ImportSceneChanges/Milliseconds": + language.ImportSceneChanges.Milliseconds = reader.Value; + break; + case "ImportText/Title": + language.ImportText.Title = reader.Value; + break; + case "ImportText/OneSubtitleIsOneFile": + language.ImportText.OneSubtitleIsOneFile = reader.Value; + break; + case "ImportText/OpenTextFile": + language.ImportText.OpenTextFile = reader.Value; + break; + case "ImportText/OpenTextFiles": + language.ImportText.OpenTextFiles = reader.Value; + break; + case "ImportText/ImportOptions": + language.ImportText.ImportOptions = reader.Value; + break; + case "ImportText/Splitting": + language.ImportText.Splitting = reader.Value; + break; + case "ImportText/AutoSplitText": + language.ImportText.AutoSplitText = reader.Value; + break; + case "ImportText/OneLineIsOneSubtitle": + language.ImportText.OneLineIsOneSubtitle = reader.Value; + break; + case "ImportText/LineBreak": + language.ImportText.LineBreak = reader.Value; + break; + case "ImportText/SplitAtBlankLines": + language.ImportText.SplitAtBlankLines = reader.Value; + break; + case "ImportText/MergeShortLines": + language.ImportText.MergeShortLines = reader.Value; + break; + case "ImportText/RemoveEmptyLines": + language.ImportText.RemoveEmptyLines = reader.Value; + break; + case "ImportText/RemoveLinesWithoutLetters": + language.ImportText.RemoveLinesWithoutLetters = reader.Value; + break; + case "ImportText/GenerateTimeCodes": + language.ImportText.GenerateTimeCodes = reader.Value; + break; + case "ImportText/GapBetweenSubtitles": + language.ImportText.GapBetweenSubtitles = reader.Value; + break; + case "ImportText/Auto": + language.ImportText.Auto = reader.Value; + break; + case "ImportText/Fixed": + language.ImportText.Fixed = reader.Value; + break; + case "ImportText/Refresh": + language.ImportText.Refresh = reader.Value; + break; + case "ImportText/TextFiles": + language.ImportText.TextFiles = reader.Value; + break; + case "ImportText/PreviewLinesModifiedX": + language.ImportText.PreviewLinesModifiedX = reader.Value; + break; + case "ImportText/TimeCodes": + language.ImportText.TimeCodes = reader.Value; + break; + case "Interjections/Title": + language.Interjections.Title = reader.Value; + break; + case "JoinSubtitles/Title": + language.JoinSubtitles.Title = reader.Value; + break; + case "JoinSubtitles/Information": + language.JoinSubtitles.Information = reader.Value; + break; + case "JoinSubtitles/NumberOfLines": + language.JoinSubtitles.NumberOfLines = reader.Value; + break; + case "JoinSubtitles/StartTime": + language.JoinSubtitles.StartTime = reader.Value; + break; + case "JoinSubtitles/EndTime": + language.JoinSubtitles.EndTime = reader.Value; + break; + case "JoinSubtitles/FileName": + language.JoinSubtitles.FileName = reader.Value; + break; + case "JoinSubtitles/Join": + language.JoinSubtitles.Join = reader.Value; + break; + case "JoinSubtitles/TotalNumberOfLinesX": + language.JoinSubtitles.TotalNumberOfLinesX = reader.Value; + break; + case "JoinSubtitles/Note": + language.JoinSubtitles.Note = reader.Value; + break; + case "Main/SaveChangesToUntitled": + language.Main.SaveChangesToUntitled = reader.Value; + break; + case "Main/SaveChangesToX": + language.Main.SaveChangesToX = reader.Value; + break; + case "Main/SaveChangesToUntitledOriginal": + language.Main.SaveChangesToUntitledOriginal = reader.Value; + break; + case "Main/SaveChangesToOriginalX": + language.Main.SaveChangesToOriginalX = reader.Value; + break; + case "Main/SaveSubtitleAs": + language.Main.SaveSubtitleAs = reader.Value; + break; + case "Main/SaveOriginalSubtitleAs": + language.Main.SaveOriginalSubtitleAs = reader.Value; + break; + case "Main/NoSubtitleLoaded": + language.Main.NoSubtitleLoaded = reader.Value; + break; + case "Main/VisualSyncSelectedLines": + language.Main.VisualSyncSelectedLines = reader.Value; + break; + case "Main/VisualSyncTitle": + language.Main.VisualSyncTitle = reader.Value; + break; + case "Main/BeforeVisualSync": + language.Main.BeforeVisualSync = reader.Value; + break; + case "Main/VisualSyncPerformedOnSelectedLines": + language.Main.VisualSyncPerformedOnSelectedLines = reader.Value; + break; + case "Main/VisualSyncPerformed": + language.Main.VisualSyncPerformed = reader.Value; + break; + case "Main/ImportThisVobSubSubtitle": + language.Main.ImportThisVobSubSubtitle = reader.Value; + break; + case "Main/FileXIsLargerThan10MB": + language.Main.FileXIsLargerThan10MB = reader.Value; + break; + case "Main/ContinueAnyway": + language.Main.ContinueAnyway = reader.Value; + break; + case "Main/BeforeLoadOf": + language.Main.BeforeLoadOf = reader.Value; + break; + case "Main/LoadedSubtitleX": + language.Main.LoadedSubtitleX = reader.Value; + break; + case "Main/LoadedEmptyOrShort": + language.Main.LoadedEmptyOrShort = reader.Value; + break; + case "Main/FileIsEmptyOrShort": + language.Main.FileIsEmptyOrShort = reader.Value; + break; + case "Main/FileNotFound": + language.Main.FileNotFound = reader.Value; + break; + case "Main/SavedSubtitleX": + language.Main.SavedSubtitleX = reader.Value; + break; + case "Main/SavedOriginalSubtitleX": + language.Main.SavedOriginalSubtitleX = reader.Value; + break; + case "Main/FileOnDiskModified": + language.Main.FileOnDiskModified = reader.Value; + break; + case "Main/OverwriteModifiedFile": + language.Main.OverwriteModifiedFile = reader.Value; + break; + case "Main/FileXIsReadOnly": + language.Main.FileXIsReadOnly = reader.Value; + break; + case "Main/UnableToSaveSubtitleX": + language.Main.UnableToSaveSubtitleX = reader.Value; + break; + case "Main/BeforeNew": + language.Main.BeforeNew = reader.Value; + break; + case "Main/New": + language.Main.New = reader.Value; + break; + case "Main/BeforeConvertingToX": + language.Main.BeforeConvertingToX = reader.Value; + break; + case "Main/ConvertedToX": + language.Main.ConvertedToX = reader.Value; + break; + case "Main/BeforeShowEarlier": + language.Main.BeforeShowEarlier = reader.Value; + break; + case "Main/BeforeShowLater": + language.Main.BeforeShowLater = reader.Value; + break; + case "Main/LineNumberX": + language.Main.LineNumberX = reader.Value; + break; + case "Main/OpenVideoFile": + language.Main.OpenVideoFile = reader.Value; + break; + case "Main/NewFrameRateUsedToCalculateTimeCodes": + language.Main.NewFrameRateUsedToCalculateTimeCodes = reader.Value; + break; + case "Main/NewFrameRateUsedToCalculateFrameNumbers": + language.Main.NewFrameRateUsedToCalculateFrameNumbers = reader.Value; + break; + case "Main/FindContinue": + language.Main.FindContinue = reader.Value; + break; + case "Main/FindContinueTitle": + language.Main.FindContinueTitle = reader.Value; + break; + case "Main/ReplaceContinueNotFound": + language.Main.ReplaceContinueNotFound = reader.Value; + break; + case "Main/ReplaceXContinue": + language.Main.ReplaceXContinue = reader.Value; + break; + case "Main/ReplaceContinueTitle": + language.Main.ReplaceContinueTitle = reader.Value; + break; + case "Main/SearchingForXFromLineY": + language.Main.SearchingForXFromLineY = reader.Value; + break; + case "Main/XFoundAtLineNumberY": + language.Main.XFoundAtLineNumberY = reader.Value; + break; + case "Main/XNotFound": + language.Main.XNotFound = reader.Value; + break; + case "Main/BeforeReplace": + language.Main.BeforeReplace = reader.Value; + break; + case "Main/MatchFoundX": + language.Main.MatchFoundX = reader.Value; + break; + case "Main/NoMatchFoundX": + language.Main.NoMatchFoundX = reader.Value; + break; + case "Main/FoundNothingToReplace": + language.Main.FoundNothingToReplace = reader.Value; + break; + case "Main/ReplaceCountX": + language.Main.ReplaceCountX = reader.Value; + break; + case "Main/NoXFoundAtLineY": + language.Main.NoXFoundAtLineY = reader.Value; + break; + case "Main/OneReplacementMade": + language.Main.OneReplacementMade = reader.Value; + break; + case "Main/BeforeChangesMadeInSourceView": + language.Main.BeforeChangesMadeInSourceView = reader.Value; + break; + case "Main/UnableToParseSourceView": + language.Main.UnableToParseSourceView = reader.Value; + break; + case "Main/GoToLineNumberX": + language.Main.GoToLineNumberX = reader.Value; + break; + case "Main/CreateAdjustChangesApplied": + language.Main.CreateAdjustChangesApplied = reader.Value; + break; + case "Main/SelectedLines": + language.Main.SelectedLines = reader.Value; + break; + case "Main/BeforeDisplayTimeAdjustment": + language.Main.BeforeDisplayTimeAdjustment = reader.Value; + break; + case "Main/DisplayTimeAdjustedX": + language.Main.DisplayTimeAdjustedX = reader.Value; + break; + case "Main/DisplayTimesAdjustedX": + language.Main.DisplayTimesAdjustedX = reader.Value; + break; + case "Main/StarTimeAdjustedX": + language.Main.StarTimeAdjustedX = reader.Value; + break; + case "Main/BeforeCommonErrorFixes": + language.Main.BeforeCommonErrorFixes = reader.Value; + break; + case "Main/CommonErrorsFixedInSelectedLines": + language.Main.CommonErrorsFixedInSelectedLines = reader.Value; + break; + case "Main/CommonErrorsFixed": + language.Main.CommonErrorsFixed = reader.Value; + break; + case "Main/BeforeRenumbering": + language.Main.BeforeRenumbering = reader.Value; + break; + case "Main/RenumberedStartingFromX": + language.Main.RenumberedStartingFromX = reader.Value; + break; + case "Main/BeforeRemovalOfTextingForHearingImpaired": + language.Main.BeforeRemovalOfTextingForHearingImpaired = reader.Value; + break; + case "Main/TextingForHearingImpairedRemovedOneLine": + language.Main.TextingForHearingImpairedRemovedOneLine = reader.Value; + break; + case "Main/TextingForHearingImpairedRemovedXLines": + language.Main.TextingForHearingImpairedRemovedXLines = reader.Value; + break; + case "Main/SubtitleSplitted": + language.Main.SubtitleSplitted = reader.Value; + break; + case "Main/SubtitleAppendPrompt": + language.Main.SubtitleAppendPrompt = reader.Value; + break; + case "Main/SubtitleAppendPromptTitle": + language.Main.SubtitleAppendPromptTitle = reader.Value; + break; + case "Main/OpenSubtitleToAppend": + language.Main.OpenSubtitleToAppend = reader.Value; + break; + case "Main/AppendViaVisualSyncTitle": + language.Main.AppendViaVisualSyncTitle = reader.Value; + break; + case "Main/AppendSynchronizedSubtitlePrompt": + language.Main.AppendSynchronizedSubtitlePrompt = reader.Value; + break; + case "Main/BeforeAppend": + language.Main.BeforeAppend = reader.Value; + break; + case "Main/SubtitleAppendedX": + language.Main.SubtitleAppendedX = reader.Value; + break; + case "Main/SubtitleNotAppended": + language.Main.SubtitleNotAppended = reader.Value; + break; + case "Main/GoogleTranslate": + language.Main.GoogleTranslate = reader.Value; + break; + case "Main/MicrosoftTranslate": + language.Main.MicrosoftTranslate = reader.Value; + break; + case "Main/BeforeGoogleTranslation": + language.Main.BeforeGoogleTranslation = reader.Value; + break; + case "Main/SelectedLinesTranslated": + language.Main.SelectedLinesTranslated = reader.Value; + break; + case "Main/SubtitleTranslated": + language.Main.SubtitleTranslated = reader.Value; + break; + case "Main/TranslateSwedishToDanish": + language.Main.TranslateSwedishToDanish = reader.Value; + break; + case "Main/TranslateSwedishToDanishWarning": + language.Main.TranslateSwedishToDanishWarning = reader.Value; + break; + case "Main/TranslatingViaNikseDkMt": + language.Main.TranslatingViaNikseDkMt = reader.Value; + break; + case "Main/BeforeSwedishToDanishTranslation": + language.Main.BeforeSwedishToDanishTranslation = reader.Value; + break; + case "Main/TranslationFromSwedishToDanishComplete": + language.Main.TranslationFromSwedishToDanishComplete = reader.Value; + break; + case "Main/TranslationFromSwedishToDanishFailed": + language.Main.TranslationFromSwedishToDanishFailed = reader.Value; + break; + case "Main/BeforeUndo": + language.Main.BeforeUndo = reader.Value; + break; + case "Main/UndoPerformed": + language.Main.UndoPerformed = reader.Value; + break; + case "Main/RedoPerformed": + language.Main.RedoPerformed = reader.Value; + break; + case "Main/NothingToUndo": + language.Main.NothingToUndo = reader.Value; + break; + case "Main/InvalidLanguageNameX": + language.Main.InvalidLanguageNameX = reader.Value; + break; + case "Main/UnableToChangeLanguage": + language.Main.UnableToChangeLanguage = reader.Value; + break; + case "Main/DoNotDisplayMessageAgain": + language.Main.DoNotDisplayMessageAgain = reader.Value; + break; + case "Main/NumberOfCorrectedWords": + language.Main.NumberOfCorrectedWords = reader.Value; + break; + case "Main/NumberOfSkippedWords": + language.Main.NumberOfSkippedWords = reader.Value; + break; + case "Main/NumberOfCorrectWords": + language.Main.NumberOfCorrectWords = reader.Value; + break; + case "Main/NumberOfWordsAddedToDictionary": + language.Main.NumberOfWordsAddedToDictionary = reader.Value; + break; + case "Main/NumberOfNameHits": + language.Main.NumberOfNameHits = reader.Value; + break; + case "Main/SpellCheck": + language.Main.SpellCheck = reader.Value; + break; + case "Main/BeforeSpellCheck": + language.Main.BeforeSpellCheck = reader.Value; + break; + case "Main/SpellCheckChangedXToY": + language.Main.SpellCheckChangedXToY = reader.Value; + break; + case "Main/BeforeAddingTagX": + language.Main.BeforeAddingTagX = reader.Value; + break; + case "Main/TagXAdded": + language.Main.TagXAdded = reader.Value; + break; + case "Main/LineXOfY": + language.Main.LineXOfY = reader.Value; + break; + case "Main/XLinesSavedAsY": + language.Main.XLinesSavedAsY = reader.Value; + break; + case "Main/XLinesDeleted": + language.Main.XLinesDeleted = reader.Value; + break; + case "Main/BeforeDeletingXLines": + language.Main.BeforeDeletingXLines = reader.Value; + break; + case "Main/DeleteXLinesPrompt": + language.Main.DeleteXLinesPrompt = reader.Value; + break; + case "Main/OneLineDeleted": + language.Main.OneLineDeleted = reader.Value; + break; + case "Main/BeforeDeletingOneLine": + language.Main.BeforeDeletingOneLine = reader.Value; + break; + case "Main/DeleteOneLinePrompt": + language.Main.DeleteOneLinePrompt = reader.Value; + break; + case "Main/BeforeInsertLine": + language.Main.BeforeInsertLine = reader.Value; + break; + case "Main/LineInserted": + language.Main.LineInserted = reader.Value; + break; + case "Main/BeforeLineUpdatedInListView": + language.Main.BeforeLineUpdatedInListView = reader.Value; + break; + case "Main/BeforeSettingFontToNormal": + language.Main.BeforeSettingFontToNormal = reader.Value; + break; + case "Main/BeforeSplitLine": + language.Main.BeforeSplitLine = reader.Value; + break; + case "Main/LineSplitted": + language.Main.LineSplitted = reader.Value; + break; + case "Main/BeforeMergeLines": + language.Main.BeforeMergeLines = reader.Value; + break; + case "Main/LinesMerged": + language.Main.LinesMerged = reader.Value; + break; + case "Main/BeforeSettingColor": + language.Main.BeforeSettingColor = reader.Value; + break; + case "Main/BeforeSettingFontName": + language.Main.BeforeSettingFontName = reader.Value; + break; + case "Main/BeforeTypeWriterEffect": + language.Main.BeforeTypeWriterEffect = reader.Value; + break; + case "Main/BeforeKaraokeEffect": + language.Main.BeforeKaraokeEffect = reader.Value; + break; + case "Main/BeforeImportingDvdSubtitle": + language.Main.BeforeImportingDvdSubtitle = reader.Value; + break; + case "Main/OpenMatroskaFile": + language.Main.OpenMatroskaFile = reader.Value; + break; + case "Main/MatroskaFiles": + language.Main.MatroskaFiles = reader.Value; + break; + case "Main/NoSubtitlesFound": + language.Main.NoSubtitlesFound = reader.Value; + break; + case "Main/NotAValidMatroskaFileX": + language.Main.NotAValidMatroskaFileX = reader.Value; + break; + case "Main/BlurayNotSubtitlesFound": + language.Main.BlurayNotSubtitlesFound = reader.Value; + break; + case "Main/ParsingMatroskaFile": + language.Main.ParsingMatroskaFile = reader.Value; + break; + case "Main/ParsingTransportStreamFile": + language.Main.ParsingTransportStreamFile = reader.Value; + break; + case "Main/BeforeImportFromMatroskaFile": + language.Main.BeforeImportFromMatroskaFile = reader.Value; + break; + case "Main/SubtitleImportedFromMatroskaFile": + language.Main.SubtitleImportedFromMatroskaFile = reader.Value; + break; + case "Main/DropFileXNotAccepted": + language.Main.DropFileXNotAccepted = reader.Value; + break; + case "Main/DropOnlyOneFile": + language.Main.DropOnlyOneFile = reader.Value; + break; + case "Main/BeforeCreateAdjustLines": + language.Main.BeforeCreateAdjustLines = reader.Value; + break; + case "Main/OpenAnsiSubtitle": + language.Main.OpenAnsiSubtitle = reader.Value; + break; + case "Main/BeforeChangeCasing": + language.Main.BeforeChangeCasing = reader.Value; + break; + case "Main/CasingCompleteMessageNoNames": + language.Main.CasingCompleteMessageNoNames = reader.Value; + break; + case "Main/CasingCompleteMessageOnlyNames": + language.Main.CasingCompleteMessageOnlyNames = reader.Value; + break; + case "Main/CasingCompleteMessage": + language.Main.CasingCompleteMessage = reader.Value; + break; + case "Main/BeforeChangeFrameRate": + language.Main.BeforeChangeFrameRate = reader.Value; + break; + case "Main/BeforeAdjustSpeedInPercent": + language.Main.BeforeAdjustSpeedInPercent = reader.Value; + break; + case "Main/FrameRateChangedFromXToY": + language.Main.FrameRateChangedFromXToY = reader.Value; + break; + case "Main/IdxFileNotFoundWarning": + language.Main.IdxFileNotFoundWarning = reader.Value; + break; + case "Main/InvalidVobSubHeader": + language.Main.InvalidVobSubHeader = reader.Value; + break; + case "Main/OpenVobSubFile": + language.Main.OpenVobSubFile = reader.Value; + break; + case "Main/VobSubFiles": + language.Main.VobSubFiles = reader.Value; + break; + case "Main/OpenBluRaySupFile": + language.Main.OpenBluRaySupFile = reader.Value; + break; + case "Main/BluRaySupFiles": + language.Main.BluRaySupFiles = reader.Value; + break; + case "Main/OpenXSubFiles": + language.Main.OpenXSubFiles = reader.Value; + break; + case "Main/XSubFiles": + language.Main.XSubFiles = reader.Value; + break; + case "Main/BeforeImportingVobSubFile": + language.Main.BeforeImportingVobSubFile = reader.Value; + break; + case "Main/BeforeImportingBluRaySupFile": + language.Main.BeforeImportingBluRaySupFile = reader.Value; + break; + case "Main/BeforeImportingBdnXml": + language.Main.BeforeImportingBdnXml = reader.Value; + break; + case "Main/BeforeShowSelectedLinesEarlierLater": + language.Main.BeforeShowSelectedLinesEarlierLater = reader.Value; + break; + case "Main/ShowAllLinesXSecondsLinesEarlier": + language.Main.ShowAllLinesXSecondsLinesEarlier = reader.Value; + break; + case "Main/ShowAllLinesXSecondsLinesLater": + language.Main.ShowAllLinesXSecondsLinesLater = reader.Value; + break; + case "Main/ShowSelectedLinesXSecondsLinesEarlier": + language.Main.ShowSelectedLinesXSecondsLinesEarlier = reader.Value; + break; + case "Main/ShowSelectedLinesXSecondsLinesLater": + language.Main.ShowSelectedLinesXSecondsLinesLater = reader.Value; + break; + case "Main/ShowSelectionAndForwardXSecondsLinesEarlier": + language.Main.ShowSelectionAndForwardXSecondsLinesEarlier = reader.Value; + break; + case "Main/ShowSelectionAndForwardXSecondsLinesLater": + language.Main.ShowSelectionAndForwardXSecondsLinesLater = reader.Value; + break; + case "Main/ShowSelectedLinesEarlierLaterPerformed": + language.Main.ShowSelectedLinesEarlierLaterPerformed = reader.Value; + break; + case "Main/DoubleWordsViaRegEx": + language.Main.DoubleWordsViaRegEx = reader.Value; + break; + case "Main/BeforeSortX": + language.Main.BeforeSortX = reader.Value; + break; + case "Main/SortedByX": + language.Main.SortedByX = reader.Value; + break; + case "Main/BeforeAutoBalanceSelectedLines": + language.Main.BeforeAutoBalanceSelectedLines = reader.Value; + break; + case "Main/NumberOfLinesAutoBalancedX": + language.Main.NumberOfLinesAutoBalancedX = reader.Value; + break; + case "Main/BeforeRemoveLineBreaksInSelectedLines": + language.Main.BeforeRemoveLineBreaksInSelectedLines = reader.Value; + break; + case "Main/NumberOfWithRemovedLineBreakX": + language.Main.NumberOfWithRemovedLineBreakX = reader.Value; + break; + case "Main/BeforeMultipleReplace": + language.Main.BeforeMultipleReplace = reader.Value; + break; + case "Main/NumberOfLinesReplacedX": + language.Main.NumberOfLinesReplacedX = reader.Value; + break; + case "Main/NameXAddedToNamesEtcList": + language.Main.NameXAddedToNamesEtcList = reader.Value; + break; + case "Main/NameXNotAddedToNamesEtcList": + language.Main.NameXNotAddedToNamesEtcList = reader.Value; + break; + case "Main/WordXAddedToUserDic": + language.Main.WordXAddedToUserDic = reader.Value; + break; + case "Main/WordXNotAddedToUserDic": + language.Main.WordXNotAddedToUserDic = reader.Value; + break; + case "Main/OcrReplacePairXAdded": + language.Main.OcrReplacePairXAdded = reader.Value; + break; + case "Main/OcrReplacePairXNotAdded": + language.Main.OcrReplacePairXNotAdded = reader.Value; + break; + case "Main/XLinesSelected": + language.Main.XLinesSelected = reader.Value; + break; + case "Main/UnicodeMusicSymbolsAnsiWarning": + language.Main.UnicodeMusicSymbolsAnsiWarning = reader.Value; + break; + case "Main/UnicodeCharactersAnsiWarning": + language.Main.UnicodeCharactersAnsiWarning = reader.Value; + break; + case "Main/NegativeTimeWarning": + language.Main.NegativeTimeWarning = reader.Value; + break; + case "Main/BeforeMergeShortLines": + language.Main.BeforeMergeShortLines = reader.Value; + break; + case "Main/BeforeSplitLongLines": + language.Main.BeforeSplitLongLines = reader.Value; + break; + case "Main/MergedShortLinesX": + language.Main.MergedShortLinesX = reader.Value; + break; + case "Main/BeforeDurationsBridgeGap": + language.Main.BeforeDurationsBridgeGap = reader.Value; + break; + case "Main/BeforeSetMinimumDisplayTimeBetweenParagraphs": + language.Main.BeforeSetMinimumDisplayTimeBetweenParagraphs = reader.Value; + break; + case "Main/XMinimumDisplayTimeBetweenParagraphsChanged": + language.Main.XMinimumDisplayTimeBetweenParagraphsChanged = reader.Value; + break; + case "Main/BeforeImportText": + language.Main.BeforeImportText = reader.Value; + break; + case "Main/TextImported": + language.Main.TextImported = reader.Value; + break; + case "Main/BeforePointSynchronization": + language.Main.BeforePointSynchronization = reader.Value; + break; + case "Main/PointSynchronizationDone": + language.Main.PointSynchronizationDone = reader.Value; + break; + case "Main/BeforeTimeCodeImport": + language.Main.BeforeTimeCodeImport = reader.Value; + break; + case "Main/TimeCodeImportedFromXY": + language.Main.TimeCodeImportedFromXY = reader.Value; + break; + case "Main/BeforeInsertSubtitleAtVideoPosition": + language.Main.BeforeInsertSubtitleAtVideoPosition = reader.Value; + break; + case "Main/BeforeSetStartTimeAndOffsetTheRest": + language.Main.BeforeSetStartTimeAndOffsetTheRest = reader.Value; + break; + case "Main/BeforeSetEndTimeAndOffsetTheRest": + language.Main.BeforeSetEndTimeAndOffsetTheRest = reader.Value; + break; + case "Main/BeforeSetEndAndVideoPosition": + language.Main.BeforeSetEndAndVideoPosition = reader.Value; + break; + case "Main/ContinueWithCurrentSpellCheck": + language.Main.ContinueWithCurrentSpellCheck = reader.Value; + break; + case "Main/CharactersPerSecond": + language.Main.CharactersPerSecond = reader.Value; + break; + case "Main/GetFrameRateFromVideoFile": + language.Main.GetFrameRateFromVideoFile = reader.Value; + break; + case "Main/NetworkMessage": + language.Main.NetworkMessage = reader.Value; + break; + case "Main/NetworkUpdate": + language.Main.NetworkUpdate = reader.Value; + break; + case "Main/NetworkInsert": + language.Main.NetworkInsert = reader.Value; + break; + case "Main/NetworkDelete": + language.Main.NetworkDelete = reader.Value; + break; + case "Main/NetworkNewUser": + language.Main.NetworkNewUser = reader.Value; + break; + case "Main/NetworkByeUser": + language.Main.NetworkByeUser = reader.Value; + break; + case "Main/NetworkUnableToConnectToServer": + language.Main.NetworkUnableToConnectToServer = reader.Value; + break; + case "Main/UserAndAction": + language.Main.UserAndAction = reader.Value; + break; + case "Main/NetworkMode": + language.Main.NetworkMode = reader.Value; + break; + case "Main/XStartedSessionYAtZ": + language.Main.XStartedSessionYAtZ = reader.Value; + break; + case "Main/SpellChekingViaWordXLineYOfX": + language.Main.SpellChekingViaWordXLineYOfX = reader.Value; + break; + case "Main/UnableToStartWord": + language.Main.UnableToStartWord = reader.Value; + break; + case "Main/SpellCheckAbortedXCorrections": + language.Main.SpellCheckAbortedXCorrections = reader.Value; + break; + case "Main/SpellCheckCompletedXCorrections": + language.Main.SpellCheckCompletedXCorrections = reader.Value; + break; + case "Main/OpenOtherSubtitle": + language.Main.OpenOtherSubtitle = reader.Value; + break; + case "Main/BeforeToggleDialogDashes": + language.Main.BeforeToggleDialogDashes = reader.Value; + break; + case "Main/ExportPlainTextAs": + language.Main.ExportPlainTextAs = reader.Value; + break; + case "Main/TextFiles": + language.Main.TextFiles = reader.Value; + break; + case "Main/SubtitleExported": + language.Main.SubtitleExported = reader.Value; + break; + case "Main/LineNumberXErrorReadingFromSourceLineY": + language.Main.LineNumberXErrorReadingFromSourceLineY = reader.Value; + break; + case "Main/LineNumberXErrorReadingTimeCodeFromSourceLineY": + language.Main.LineNumberXErrorReadingTimeCodeFromSourceLineY = reader.Value; + break; + case "Main/LineNumberXExpectedNumberFromSourceLineY": + language.Main.LineNumberXExpectedNumberFromSourceLineY = reader.Value; + break; + case "Main/BeforeGuessingTimeCodes": + language.Main.BeforeGuessingTimeCodes = reader.Value; + break; + case "Main/BeforeAutoDuration": + language.Main.BeforeAutoDuration = reader.Value; + break; + case "Main/BeforeColumnPaste": + language.Main.BeforeColumnPaste = reader.Value; + break; + case "Main/BeforeColumnDelete": + language.Main.BeforeColumnDelete = reader.Value; + break; + case "Main/BeforeColumnImportText": + language.Main.BeforeColumnImportText = reader.Value; + break; + case "Main/BeforeColumnShiftCellsDown": + language.Main.BeforeColumnShiftCellsDown = reader.Value; + break; + case "Main/ErrorLoadingPluginXErrorY": + language.Main.ErrorLoadingPluginXErrorY = reader.Value; + break; + case "Main/BeforeRunningPluginXVersionY": + language.Main.BeforeRunningPluginXVersionY = reader.Value; + break; + case "Main/UnableToReadPluginResult": + language.Main.UnableToReadPluginResult = reader.Value; + break; + case "Main/UnableToCreateBackupDirectory": + language.Main.UnableToCreateBackupDirectory = reader.Value; + break; + case "Main/BeforeDisplaySubtitleJoin": + language.Main.BeforeDisplaySubtitleJoin = reader.Value; + break; + case "Main/SubtitlesJoined": + language.Main.SubtitlesJoined = reader.Value; + break; + case "Main/StatusLog": + language.Main.StatusLog = reader.Value; + break; + case "Main/XSceneChangesImported": + language.Main.XSceneChangesImported = reader.Value; + break; + case "Main/PluginXExecuted": + language.Main.PluginXExecuted = reader.Value; + break; + case "Main/NotAValidXSubFile": + language.Main.NotAValidXSubFile = reader.Value; + break; + case "Main/BeforeMergeLinesWithSameText": + language.Main.BeforeMergeLinesWithSameText = reader.Value; + break; + case "Main/ImportTimeCodesDifferentNumberOfLinesWarning": + language.Main.ImportTimeCodesDifferentNumberOfLinesWarning = reader.Value; + break; + case "Main/ParsingTransportStream": + language.Main.ParsingTransportStream = reader.Value; + break; + case "Main/XPercentCompleted": + language.Main.XPercentCompleted = reader.Value; + break; + case "Main/ErrorLoadIdx": + language.Main.ErrorLoadIdx = reader.Value; + break; + case "Main/ErrorLoadRar": + language.Main.ErrorLoadRar = reader.Value; + break; + case "Main/ErrorLoadZip": + language.Main.ErrorLoadZip = reader.Value; + break; + case "Main/ErrorLoadPng": + language.Main.ErrorLoadPng = reader.Value; + break; + case "Main/ErrorLoadJpg": + language.Main.ErrorLoadJpg = reader.Value; + break; + case "Main/ErrorLoadSrr": + language.Main.ErrorLoadSrr = reader.Value; + break; + case "Main/ErrorLoadTorrent": + language.Main.ErrorLoadTorrent = reader.Value; + break; + case "Main/ErrorLoadBinaryZeroes": + language.Main.ErrorLoadBinaryZeroes = reader.Value; + break; + case "Main/ErrorDirectoryDropNotAllowed": + language.Main.ErrorDirectoryDropNotAllowed = reader.Value; + break; + case "Main/NoSupportEncryptedVobSub": + language.Main.NoSupportEncryptedVobSub = reader.Value; + break; + case "Main/NoSupportHereBluRaySup": + language.Main.NoSupportHereBluRaySup = reader.Value; + break; + case "Main/NoSupportHereDvdSup": + language.Main.NoSupportHereDvdSup = reader.Value; + break; + case "Main/NoSupportHereVobSub": + language.Main.NoSupportHereVobSub = reader.Value; + break; + case "Main/NoSupportHereDivx": + language.Main.NoSupportHereDivx = reader.Value; + break; + case "Main/Menu/File/Title": + language.Main.Menu.File.Title = reader.Value; + break; + case "Main/Menu/File/New": + language.Main.Menu.File.New = reader.Value; + break; + case "Main/Menu/File/Open": + language.Main.Menu.File.Open = reader.Value; + break; + case "Main/Menu/File/OpenKeepVideo": + language.Main.Menu.File.OpenKeepVideo = reader.Value; + break; + case "Main/Menu/File/Reopen": + language.Main.Menu.File.Reopen = reader.Value; + break; + case "Main/Menu/File/Save": + language.Main.Menu.File.Save = reader.Value; + break; + case "Main/Menu/File/SaveAs": + language.Main.Menu.File.SaveAs = reader.Value; + break; + case "Main/Menu/File/RestoreAutoBackup": + language.Main.Menu.File.RestoreAutoBackup = reader.Value; + break; + case "Main/Menu/File/AdvancedSubStationAlphaProperties": + language.Main.Menu.File.AdvancedSubStationAlphaProperties = reader.Value; + break; + case "Main/Menu/File/SubStationAlphaProperties": + language.Main.Menu.File.SubStationAlphaProperties = reader.Value; + break; + case "Main/Menu/File/EbuProperties": + language.Main.Menu.File.EbuProperties = reader.Value; + break; + case "Main/Menu/File/PacProperties": + language.Main.Menu.File.PacProperties = reader.Value; + break; + case "Main/Menu/File/OpenOriginal": + language.Main.Menu.File.OpenOriginal = reader.Value; + break; + case "Main/Menu/File/SaveOriginal": + language.Main.Menu.File.SaveOriginal = reader.Value; + break; + case "Main/Menu/File/CloseOriginal": + language.Main.Menu.File.CloseOriginal = reader.Value; + break; + case "Main/Menu/File/OpenContainingFolder": + language.Main.Menu.File.OpenContainingFolder = reader.Value; + break; + case "Main/Menu/File/Compare": + language.Main.Menu.File.Compare = reader.Value; + break; + case "Main/Menu/File/Statistics": + language.Main.Menu.File.Statistics = reader.Value; + break; + case "Main/Menu/File/Plugins": + language.Main.Menu.File.Plugins = reader.Value; + break; + case "Main/Menu/File/ImportOcrFromDvd": + language.Main.Menu.File.ImportOcrFromDvd = reader.Value; + break; + case "Main/Menu/File/ImportOcrVobSubSubtitle": + language.Main.Menu.File.ImportOcrVobSubSubtitle = reader.Value; + break; + case "Main/Menu/File/ImportBluRaySupFile": + language.Main.Menu.File.ImportBluRaySupFile = reader.Value; + break; + case "Main/Menu/File/ImportXSub": + language.Main.Menu.File.ImportXSub = reader.Value; + break; + case "Main/Menu/File/ImportSubtitleFromMatroskaFile": + language.Main.Menu.File.ImportSubtitleFromMatroskaFile = reader.Value; + break; + case "Main/Menu/File/ImportSubtitleWithManualChosenEncoding": + language.Main.Menu.File.ImportSubtitleWithManualChosenEncoding = reader.Value; + break; + case "Main/Menu/File/ImportText": + language.Main.Menu.File.ImportText = reader.Value; + break; + case "Main/Menu/File/ImportImages": + language.Main.Menu.File.ImportImages = reader.Value; + break; + case "Main/Menu/File/ImportTimecodes": + language.Main.Menu.File.ImportTimecodes = reader.Value; + break; + case "Main/Menu/File/Export": + language.Main.Menu.File.Export = reader.Value; + break; + case "Main/Menu/File/ExportBdnXml": + language.Main.Menu.File.ExportBdnXml = reader.Value; + break; + case "Main/Menu/File/ExportBluRaySup": + language.Main.Menu.File.ExportBluRaySup = reader.Value; + break; + case "Main/Menu/File/ExportVobSub": + language.Main.Menu.File.ExportVobSub = reader.Value; + break; + case "Main/Menu/File/ExportCavena890": + language.Main.Menu.File.ExportCavena890 = reader.Value; + break; + case "Main/Menu/File/ExportEbu": + language.Main.Menu.File.ExportEbu = reader.Value; + break; + case "Main/Menu/File/ExportPac": + language.Main.Menu.File.ExportPac = reader.Value; + break; + case "Main/Menu/File/ExportPlainText": + language.Main.Menu.File.ExportPlainText = reader.Value; + break; + case "Main/Menu/File/ExportAdobeEncoreFabImageScript": + language.Main.Menu.File.ExportAdobeEncoreFabImageScript = reader.Value; + break; + case "Main/Menu/File/ExportKoreanAtsFilePair": + language.Main.Menu.File.ExportKoreanAtsFilePair = reader.Value; + break; + case "Main/Menu/File/ExportAvidStl": + language.Main.Menu.File.ExportAvidStl = reader.Value; + break; + case "Main/Menu/File/ExportDvdStudioProStl": + language.Main.Menu.File.ExportDvdStudioProStl = reader.Value; + break; + case "Main/Menu/File/ExportCapMakerPlus": + language.Main.Menu.File.ExportCapMakerPlus = reader.Value; + break; + case "Main/Menu/File/ExportCaptionsInc": + language.Main.Menu.File.ExportCaptionsInc = reader.Value; + break; + case "Main/Menu/File/ExportCheetahCap": + language.Main.Menu.File.ExportCheetahCap = reader.Value; + break; + case "Main/Menu/File/ExportUltech130": + language.Main.Menu.File.ExportUltech130 = reader.Value; + break; + case "Main/Menu/File/ExportCustomTextFormat": + language.Main.Menu.File.ExportCustomTextFormat = reader.Value; + break; + case "Main/Menu/File/Exit": + language.Main.Menu.File.Exit = reader.Value; + break; + case "Main/Menu/Edit/Title": + language.Main.Menu.Edit.Title = reader.Value; + break; + case "Main/Menu/Edit/Undo": + language.Main.Menu.Edit.Undo = reader.Value; + break; + case "Main/Menu/Edit/Redo": + language.Main.Menu.Edit.Redo = reader.Value; + break; + case "Main/Menu/Edit/ShowUndoHistory": + language.Main.Menu.Edit.ShowUndoHistory = reader.Value; + break; + case "Main/Menu/Edit/InsertUnicodeSymbol": + language.Main.Menu.Edit.InsertUnicodeSymbol = reader.Value; + break; + case "Main/Menu/Edit/InsertUnicodeControlCharacters": + language.Main.Menu.Edit.InsertUnicodeControlCharacters = reader.Value; + break; + case "Main/Menu/Edit/InsertUnicodeControlCharactersLRM": + language.Main.Menu.Edit.InsertUnicodeControlCharactersLRM = reader.Value; + break; + case "Main/Menu/Edit/InsertUnicodeControlCharactersRLM": + language.Main.Menu.Edit.InsertUnicodeControlCharactersRLM = reader.Value; + break; + case "Main/Menu/Edit/InsertUnicodeControlCharactersLRE": + language.Main.Menu.Edit.InsertUnicodeControlCharactersLRE = reader.Value; + break; + case "Main/Menu/Edit/InsertUnicodeControlCharactersRLE": + language.Main.Menu.Edit.InsertUnicodeControlCharactersRLE = reader.Value; + break; + case "Main/Menu/Edit/InsertUnicodeControlCharactersLRO": + language.Main.Menu.Edit.InsertUnicodeControlCharactersLRO = reader.Value; + break; + case "Main/Menu/Edit/InsertUnicodeControlCharactersRLO": + language.Main.Menu.Edit.InsertUnicodeControlCharactersRLO = reader.Value; + break; + case "Main/Menu/Edit/Find": + language.Main.Menu.Edit.Find = reader.Value; + break; + case "Main/Menu/Edit/FindNext": + language.Main.Menu.Edit.FindNext = reader.Value; + break; + case "Main/Menu/Edit/Replace": + language.Main.Menu.Edit.Replace = reader.Value; + break; + case "Main/Menu/Edit/MultipleReplace": + language.Main.Menu.Edit.MultipleReplace = reader.Value; + break; + case "Main/Menu/Edit/GoToSubtitleNumber": + language.Main.Menu.Edit.GoToSubtitleNumber = reader.Value; + break; + case "Main/Menu/Edit/RightToLeftMode": + language.Main.Menu.Edit.RightToLeftMode = reader.Value; + break; + case "Main/Menu/Edit/FixTrlViaUnicodeControlCharacters": + language.Main.Menu.Edit.FixTrlViaUnicodeControlCharacters = reader.Value; + break; + case "Main/Menu/Edit/ReverseRightToLeftStartEnd": + language.Main.Menu.Edit.ReverseRightToLeftStartEnd = reader.Value; + break; + case "Main/Menu/Edit/ShowOriginalTextInAudioAndVideoPreview": + language.Main.Menu.Edit.ShowOriginalTextInAudioAndVideoPreview = reader.Value; + break; + case "Main/Menu/Edit/ModifySelection": + language.Main.Menu.Edit.ModifySelection = reader.Value; + break; + case "Main/Menu/Edit/InverseSelection": + language.Main.Menu.Edit.InverseSelection = reader.Value; + break; + case "Main/Menu/Tools/Title": + language.Main.Menu.Tools.Title = reader.Value; + break; + case "Main/Menu/Tools/AdjustDisplayDuration": + language.Main.Menu.Tools.AdjustDisplayDuration = reader.Value; + break; + case "Main/Menu/Tools/ApplyDurationLimits": + language.Main.Menu.Tools.ApplyDurationLimits = reader.Value; + break; + case "Main/Menu/Tools/DurationsBridgeGap": + language.Main.Menu.Tools.DurationsBridgeGap = reader.Value; + break; + case "Main/Menu/Tools/FixCommonErrors": + language.Main.Menu.Tools.FixCommonErrors = reader.Value; + break; + case "Main/Menu/Tools/StartNumberingFrom": + language.Main.Menu.Tools.StartNumberingFrom = reader.Value; + break; + case "Main/Menu/Tools/RemoveTextForHearingImpaired": + language.Main.Menu.Tools.RemoveTextForHearingImpaired = reader.Value; + break; + case "Main/Menu/Tools/ChangeCasing": + language.Main.Menu.Tools.ChangeCasing = reader.Value; + break; + case "Main/Menu/Tools/ChangeFrameRate": + language.Main.Menu.Tools.ChangeFrameRate = reader.Value; + break; + case "Main/Menu/Tools/ChangeSpeedInPercent": + language.Main.Menu.Tools.ChangeSpeedInPercent = reader.Value; + break; + case "Main/Menu/Tools/MergeShortLines": + language.Main.Menu.Tools.MergeShortLines = reader.Value; + break; + case "Main/Menu/Tools/MergeDuplicateText": + language.Main.Menu.Tools.MergeDuplicateText = reader.Value; + break; + case "Main/Menu/Tools/MergeSameTimeCodes": + language.Main.Menu.Tools.MergeSameTimeCodes = reader.Value; + break; + case "Main/Menu/Tools/SplitLongLines": + language.Main.Menu.Tools.SplitLongLines = reader.Value; + break; + case "Main/Menu/Tools/MinimumDisplayTimeBetweenParagraphs": + language.Main.Menu.Tools.MinimumDisplayTimeBetweenParagraphs = reader.Value; + break; + case "Main/Menu/Tools/SortBy": + language.Main.Menu.Tools.SortBy = reader.Value; + break; + case "Main/Menu/Tools/Number": + language.Main.Menu.Tools.Number = reader.Value; + break; + case "Main/Menu/Tools/StartTime": + language.Main.Menu.Tools.StartTime = reader.Value; + break; + case "Main/Menu/Tools/EndTime": + language.Main.Menu.Tools.EndTime = reader.Value; + break; + case "Main/Menu/Tools/Duration": + language.Main.Menu.Tools.Duration = reader.Value; + break; + case "Main/Menu/Tools/TextAlphabetically": + language.Main.Menu.Tools.TextAlphabetically = reader.Value; + break; + case "Main/Menu/Tools/TextSingleLineMaximumLength": + language.Main.Menu.Tools.TextSingleLineMaximumLength = reader.Value; + break; + case "Main/Menu/Tools/TextTotalLength": + language.Main.Menu.Tools.TextTotalLength = reader.Value; + break; + case "Main/Menu/Tools/TextNumberOfLines": + language.Main.Menu.Tools.TextNumberOfLines = reader.Value; + break; + case "Main/Menu/Tools/TextNumberOfCharactersPerSeconds": + language.Main.Menu.Tools.TextNumberOfCharactersPerSeconds = reader.Value; + break; + case "Main/Menu/Tools/WordsPerMinute": + language.Main.Menu.Tools.WordsPerMinute = reader.Value; + break; + case "Main/Menu/Tools/Style": + language.Main.Menu.Tools.Style = reader.Value; + break; + case "Main/Menu/Tools/Ascending": + language.Main.Menu.Tools.Ascending = reader.Value; + break; + case "Main/Menu/Tools/Descending": + language.Main.Menu.Tools.Descending = reader.Value; + break; + case "Main/Menu/Tools/MakeNewEmptyTranslationFromCurrentSubtitle": + language.Main.Menu.Tools.MakeNewEmptyTranslationFromCurrentSubtitle = reader.Value; + break; + case "Main/Menu/Tools/BatchConvert": + language.Main.Menu.Tools.BatchConvert = reader.Value; + break; + case "Main/Menu/Tools/GenerateTimeAsText": + language.Main.Menu.Tools.GenerateTimeAsText = reader.Value; + break; + case "Main/Menu/Tools/MeasurementConverter": + language.Main.Menu.Tools.MeasurementConverter = reader.Value; + break; + case "Main/Menu/Tools/SplitSubtitle": + language.Main.Menu.Tools.SplitSubtitle = reader.Value; + break; + case "Main/Menu/Tools/AppendSubtitle": + language.Main.Menu.Tools.AppendSubtitle = reader.Value; + break; + case "Main/Menu/Tools/JoinSubtitles": + language.Main.Menu.Tools.JoinSubtitles = reader.Value; + break; + case "Main/Menu/Video/Title": + language.Main.Menu.Video.Title = reader.Value; + break; + case "Main/Menu/Video/OpenVideo": + language.Main.Menu.Video.OpenVideo = reader.Value; + break; + case "Main/Menu/Video/OpenDvd": + language.Main.Menu.Video.OpenDvd = reader.Value; + break; + case "Main/Menu/Video/ChooseAudioTrack": + language.Main.Menu.Video.ChooseAudioTrack = reader.Value; + break; + case "Main/Menu/Video/CloseVideo": + language.Main.Menu.Video.CloseVideo = reader.Value; + break; + case "Main/Menu/Video/ImportSceneChanges": + language.Main.Menu.Video.ImportSceneChanges = reader.Value; + break; + case "Main/Menu/Video/RemoveSceneChanges": + language.Main.Menu.Video.RemoveSceneChanges = reader.Value; + break; + case "Main/Menu/Video/WaveformBatchGenerate": + language.Main.Menu.Video.WaveformBatchGenerate = reader.Value; + break; + case "Main/Menu/Video/ShowHideVideo": + language.Main.Menu.Video.ShowHideVideo = reader.Value; + break; + case "Main/Menu/Video/ShowHideWaveform": + language.Main.Menu.Video.ShowHideWaveform = reader.Value; + break; + case "Main/Menu/Video/ShowHideWaveformAndSpectrogram": + language.Main.Menu.Video.ShowHideWaveformAndSpectrogram = reader.Value; + break; + case "Main/Menu/Video/UnDockVideoControls": + language.Main.Menu.Video.UnDockVideoControls = reader.Value; + break; + case "Main/Menu/Video/ReDockVideoControls": + language.Main.Menu.Video.ReDockVideoControls = reader.Value; + break; + case "Main/Menu/SpellCheck/Title": + language.Main.Menu.SpellCheck.Title = reader.Value; + break; + case "Main/Menu/SpellCheck/SpellCheck": + language.Main.Menu.SpellCheck.SpellCheck = reader.Value; + break; + case "Main/Menu/SpellCheck/SpellCheckFromCurrentLine": + language.Main.Menu.SpellCheck.SpellCheckFromCurrentLine = reader.Value; + break; + case "Main/Menu/SpellCheck/FindDoubleWords": + language.Main.Menu.SpellCheck.FindDoubleWords = reader.Value; + break; + case "Main/Menu/SpellCheck/FindDoubleLines": + language.Main.Menu.SpellCheck.FindDoubleLines = reader.Value; + break; + case "Main/Menu/SpellCheck/GetDictionaries": + language.Main.Menu.SpellCheck.GetDictionaries = reader.Value; + break; + case "Main/Menu/SpellCheck/AddToNamesEtcList": + language.Main.Menu.SpellCheck.AddToNamesEtcList = reader.Value; + break; + case "Main/Menu/Synchronization/Title": + language.Main.Menu.Synchronization.Title = reader.Value; + break; + case "Main/Menu/Synchronization/AdjustAllTimes": + language.Main.Menu.Synchronization.AdjustAllTimes = reader.Value; + break; + case "Main/Menu/Synchronization/VisualSync": + language.Main.Menu.Synchronization.VisualSync = reader.Value; + break; + case "Main/Menu/Synchronization/PointSync": + language.Main.Menu.Synchronization.PointSync = reader.Value; + break; + case "Main/Menu/Synchronization/PointSyncViaOtherSubtitle": + language.Main.Menu.Synchronization.PointSyncViaOtherSubtitle = reader.Value; + break; + case "Main/Menu/AutoTranslate/Title": + language.Main.Menu.AutoTranslate.Title = reader.Value; + break; + case "Main/Menu/AutoTranslate/TranslatePoweredByGoogle": + language.Main.Menu.AutoTranslate.TranslatePoweredByGoogle = reader.Value; + break; + case "Main/Menu/AutoTranslate/TranslatePoweredByMicrosoft": + language.Main.Menu.AutoTranslate.TranslatePoweredByMicrosoft = reader.Value; + break; + case "Main/Menu/AutoTranslate/TranslateFromSwedishToDanish": + language.Main.Menu.AutoTranslate.TranslateFromSwedishToDanish = reader.Value; + break; + case "Main/Menu/Options/Title": + language.Main.Menu.Options.Title = reader.Value; + break; + case "Main/Menu/Options/Settings": + language.Main.Menu.Options.Settings = reader.Value; + break; + case "Main/Menu/Options/ChooseLanguage": + language.Main.Menu.Options.ChooseLanguage = reader.Value; + break; + case "Main/Menu/Networking/Title": + language.Main.Menu.Networking.Title = reader.Value; + break; + case "Main/Menu/Networking/StartNewSession": + language.Main.Menu.Networking.StartNewSession = reader.Value; + break; + case "Main/Menu/Networking/JoinSession": + language.Main.Menu.Networking.JoinSession = reader.Value; + break; + case "Main/Menu/Networking/ShowSessionInfoAndLog": + language.Main.Menu.Networking.ShowSessionInfoAndLog = reader.Value; + break; + case "Main/Menu/Networking/Chat": + language.Main.Menu.Networking.Chat = reader.Value; + break; + case "Main/Menu/Networking/LeaveSession": + language.Main.Menu.Networking.LeaveSession = reader.Value; + break; + case "Main/Menu/Help/CheckForUpdates": + language.Main.Menu.Help.CheckForUpdates = reader.Value; + break; + case "Main/Menu/Help/Title": + language.Main.Menu.Help.Title = reader.Value; + break; + case "Main/Menu/Help/Help": + language.Main.Menu.Help.Help = reader.Value; + break; + case "Main/Menu/Help/About": + language.Main.Menu.Help.About = reader.Value; + break; + case "Main/Menu/ToolBar/New": + language.Main.Menu.ToolBar.New = reader.Value; + break; + case "Main/Menu/ToolBar/Open": + language.Main.Menu.ToolBar.Open = reader.Value; + break; + case "Main/Menu/ToolBar/Save": + language.Main.Menu.ToolBar.Save = reader.Value; + break; + case "Main/Menu/ToolBar/SaveAs": + language.Main.Menu.ToolBar.SaveAs = reader.Value; + break; + case "Main/Menu/ToolBar/Find": + language.Main.Menu.ToolBar.Find = reader.Value; + break; + case "Main/Menu/ToolBar/Replace": + language.Main.Menu.ToolBar.Replace = reader.Value; + break; + case "Main/Menu/ToolBar/FixCommonErrors": + language.Main.Menu.ToolBar.FixCommonErrors = reader.Value; + break; + case "Main/Menu/ToolBar/VisualSync": + language.Main.Menu.ToolBar.VisualSync = reader.Value; + break; + case "Main/Menu/ToolBar/SpellCheck": + language.Main.Menu.ToolBar.SpellCheck = reader.Value; + break; + case "Main/Menu/ToolBar/Settings": + language.Main.Menu.ToolBar.Settings = reader.Value; + break; + case "Main/Menu/ToolBar/Help": + language.Main.Menu.ToolBar.Help = reader.Value; + break; + case "Main/Menu/ToolBar/ShowHideWaveform": + language.Main.Menu.ToolBar.ShowHideWaveform = reader.Value; + break; + case "Main/Menu/ToolBar/ShowHideVideo": + language.Main.Menu.ToolBar.ShowHideVideo = reader.Value; + break; + case "Main/Menu/ContextMenu/AdvancedSubStationAlphaSetStyle": + language.Main.Menu.ContextMenu.AdvancedSubStationAlphaSetStyle = reader.Value; + break; + case "Main/Menu/ContextMenu/SubStationAlphaSetStyle": + language.Main.Menu.ContextMenu.SubStationAlphaSetStyle = reader.Value; + break; + case "Main/Menu/ContextMenu/SubStationAlphaStyles": + language.Main.Menu.ContextMenu.SubStationAlphaStyles = reader.Value; + break; + case "Main/Menu/ContextMenu/AdvancedSubStationAlphaStyles": + language.Main.Menu.ContextMenu.AdvancedSubStationAlphaStyles = reader.Value; + break; + case "Main/Menu/ContextMenu/TimedTextSetStyle": + language.Main.Menu.ContextMenu.TimedTextSetStyle = reader.Value; + break; + case "Main/Menu/ContextMenu/TimedTextStyles": + language.Main.Menu.ContextMenu.TimedTextStyles = reader.Value; + break; + case "Main/Menu/ContextMenu/TimedTextSetLanguage": + language.Main.Menu.ContextMenu.TimedTextSetLanguage = reader.Value; + break; + case "Main/Menu/ContextMenu/SamiSetStyle": + language.Main.Menu.ContextMenu.SamiSetStyle = reader.Value; + break; + case "Main/Menu/ContextMenu/NuendoSetStyle": + language.Main.Menu.ContextMenu.NuendoSetStyle = reader.Value; + break; + case "Main/Menu/ContextMenu/Cut": + language.Main.Menu.ContextMenu.Cut = reader.Value; + break; + case "Main/Menu/ContextMenu/Copy": + language.Main.Menu.ContextMenu.Copy = reader.Value; + break; + case "Main/Menu/ContextMenu/Paste": + language.Main.Menu.ContextMenu.Paste = reader.Value; + break; + case "Main/Menu/ContextMenu/Delete": + language.Main.Menu.ContextMenu.Delete = reader.Value; + break; + case "Main/Menu/ContextMenu/SplitLineAtCursorPosition": + language.Main.Menu.ContextMenu.SplitLineAtCursorPosition = reader.Value; + break; + case "Main/Menu/ContextMenu/AutoDurationCurrentLine": + language.Main.Menu.ContextMenu.AutoDurationCurrentLine = reader.Value; + break; + case "Main/Menu/ContextMenu/SelectAll": + language.Main.Menu.ContextMenu.SelectAll = reader.Value; + break; + case "Main/Menu/ContextMenu/InsertFirstLine": + language.Main.Menu.ContextMenu.InsertFirstLine = reader.Value; + break; + case "Main/Menu/ContextMenu/InsertBefore": + language.Main.Menu.ContextMenu.InsertBefore = reader.Value; + break; + case "Main/Menu/ContextMenu/InsertAfter": + language.Main.Menu.ContextMenu.InsertAfter = reader.Value; + break; + case "Main/Menu/ContextMenu/InsertSubtitleAfter": + language.Main.Menu.ContextMenu.InsertSubtitleAfter = reader.Value; + break; + case "Main/Menu/ContextMenu/CopyToClipboard": + language.Main.Menu.ContextMenu.CopyToClipboard = reader.Value; + break; + case "Main/Menu/ContextMenu/Column": + language.Main.Menu.ContextMenu.Column = reader.Value; + break; + case "Main/Menu/ContextMenu/ColumnDeleteText": + language.Main.Menu.ContextMenu.ColumnDeleteText = reader.Value; + break; + case "Main/Menu/ContextMenu/ColumnDeleteTextAndShiftCellsUp": + language.Main.Menu.ContextMenu.ColumnDeleteTextAndShiftCellsUp = reader.Value; + break; + case "Main/Menu/ContextMenu/ColumnInsertEmptyTextAndShiftCellsDown": + language.Main.Menu.ContextMenu.ColumnInsertEmptyTextAndShiftCellsDown = reader.Value; + break; + case "Main/Menu/ContextMenu/ColumnInsertTextFromSubtitle": + language.Main.Menu.ContextMenu.ColumnInsertTextFromSubtitle = reader.Value; + break; + case "Main/Menu/ContextMenu/ColumnImportTextAndShiftCellsDown": + language.Main.Menu.ContextMenu.ColumnImportTextAndShiftCellsDown = reader.Value; + break; + case "Main/Menu/ContextMenu/ColumnPasteFromClipboard": + language.Main.Menu.ContextMenu.ColumnPasteFromClipboard = reader.Value; + break; + case "Main/Menu/ContextMenu/ColumnCopyOriginalTextToCurrent": + language.Main.Menu.ContextMenu.ColumnCopyOriginalTextToCurrent = reader.Value; + break; + case "Main/Menu/ContextMenu/Split": + language.Main.Menu.ContextMenu.Split = reader.Value; + break; + case "Main/Menu/ContextMenu/MergeSelectedLines": + language.Main.Menu.ContextMenu.MergeSelectedLines = reader.Value; + break; + case "Main/Menu/ContextMenu/MergeSelectedLinesAsDialog": + language.Main.Menu.ContextMenu.MergeSelectedLinesAsDialog = reader.Value; + break; + case "Main/Menu/ContextMenu/MergeWithLineBefore": + language.Main.Menu.ContextMenu.MergeWithLineBefore = reader.Value; + break; + case "Main/Menu/ContextMenu/MergeWithLineAfter": + language.Main.Menu.ContextMenu.MergeWithLineAfter = reader.Value; + break; + case "Main/Menu/ContextMenu/Normal": + language.Main.Menu.ContextMenu.Normal = reader.Value; + break; + case "Main/Menu/ContextMenu/Underline": + language.Main.Menu.ContextMenu.Underline = reader.Value; + break; + case "Main/Menu/ContextMenu/Color": + language.Main.Menu.ContextMenu.Color = reader.Value; + break; + case "Main/Menu/ContextMenu/FontName": + language.Main.Menu.ContextMenu.FontName = reader.Value; + break; + case "Main/Menu/ContextMenu/Alignment": + language.Main.Menu.ContextMenu.Alignment = reader.Value; + break; + case "Main/Menu/ContextMenu/AutoBalanceSelectedLines": + language.Main.Menu.ContextMenu.AutoBalanceSelectedLines = reader.Value; + break; + case "Main/Menu/ContextMenu/RemoveLineBreaksFromSelectedLines": + language.Main.Menu.ContextMenu.RemoveLineBreaksFromSelectedLines = reader.Value; + break; + case "Main/Menu/ContextMenu/TypewriterEffect": + language.Main.Menu.ContextMenu.TypewriterEffect = reader.Value; + break; + case "Main/Menu/ContextMenu/KaraokeEffect": + language.Main.Menu.ContextMenu.KaraokeEffect = reader.Value; + break; + case "Main/Menu/ContextMenu/ShowSelectedLinesEarlierLater": + language.Main.Menu.ContextMenu.ShowSelectedLinesEarlierLater = reader.Value; + break; + case "Main/Menu/ContextMenu/VisualSyncSelectedLines": + language.Main.Menu.ContextMenu.VisualSyncSelectedLines = reader.Value; + break; + case "Main/Menu/ContextMenu/GoogleAndMicrosoftTranslateSelectedLine": + language.Main.Menu.ContextMenu.GoogleAndMicrosoftTranslateSelectedLine = reader.Value; + break; + case "Main/Menu/ContextMenu/GoogleTranslateSelectedLines": + language.Main.Menu.ContextMenu.GoogleTranslateSelectedLines = reader.Value; + break; + case "Main/Menu/ContextMenu/AdjustDisplayDurationForSelectedLines": + language.Main.Menu.ContextMenu.AdjustDisplayDurationForSelectedLines = reader.Value; + break; + case "Main/Menu/ContextMenu/FixCommonErrorsInSelectedLines": + language.Main.Menu.ContextMenu.FixCommonErrorsInSelectedLines = reader.Value; + break; + case "Main/Menu/ContextMenu/ChangeCasingForSelectedLines": + language.Main.Menu.ContextMenu.ChangeCasingForSelectedLines = reader.Value; + break; + case "Main/Menu/ContextMenu/SaveSelectedLines": + language.Main.Menu.ContextMenu.SaveSelectedLines = reader.Value; + break; + case "Main/Menu/ContextMenu/WebVTTSetNewVoice": + language.Main.Menu.ContextMenu.WebVTTSetNewVoice = reader.Value; + break; + case "Main/Menu/ContextMenu/WebVTTRemoveVoices": + language.Main.Menu.ContextMenu.WebVTTRemoveVoices = reader.Value; + break; + case "Main/Controls/SubtitleFormat": + language.Main.Controls.SubtitleFormat = reader.Value; + break; + case "Main/Controls/FileEncoding": + language.Main.Controls.FileEncoding = reader.Value; + break; + case "Main/Controls/ListView": + language.Main.Controls.ListView = reader.Value; + break; + case "Main/Controls/SourceView": + language.Main.Controls.SourceView = reader.Value; + break; + case "Main/Controls/UndoChangesInEditPanel": + language.Main.Controls.UndoChangesInEditPanel = reader.Value; + break; + case "Main/Controls/Previous": + language.Main.Controls.Previous = reader.Value; + break; + case "Main/Controls/Next": + language.Main.Controls.Next = reader.Value; + break; + case "Main/Controls/AutoBreak": + language.Main.Controls.AutoBreak = reader.Value; + break; + case "Main/Controls/Unbreak": + language.Main.Controls.Unbreak = reader.Value; + break; + case "Main/VideoControls/Translate": + language.Main.VideoControls.Translate = reader.Value; + break; + case "Main/VideoControls/Create": + language.Main.VideoControls.Create = reader.Value; + break; + case "Main/VideoControls/Adjust": + language.Main.VideoControls.Adjust = reader.Value; + break; + case "Main/VideoControls/SelectCurrentElementWhilePlaying": + language.Main.VideoControls.SelectCurrentElementWhilePlaying = reader.Value; + break; + case "Main/VideoControls/AutoRepeat": + language.Main.VideoControls.AutoRepeat = reader.Value; + break; + case "Main/VideoControls/AutoRepeatOn": + language.Main.VideoControls.AutoRepeatOn = reader.Value; + break; + case "Main/VideoControls/AutoRepeatCount": + language.Main.VideoControls.AutoRepeatCount = reader.Value; + break; + case "Main/VideoControls/AutoContinue": + language.Main.VideoControls.AutoContinue = reader.Value; + break; + case "Main/VideoControls/AutoContinueOn": + language.Main.VideoControls.AutoContinueOn = reader.Value; + break; + case "Main/VideoControls/DelayInSeconds": + language.Main.VideoControls.DelayInSeconds = reader.Value; + break; + case "Main/VideoControls/OriginalText": + language.Main.VideoControls.OriginalText = reader.Value; + break; + case "Main/VideoControls/Previous": + language.Main.VideoControls.Previous = reader.Value; + break; + case "Main/VideoControls/Stop": + language.Main.VideoControls.Stop = reader.Value; + break; + case "Main/VideoControls/PlayCurrent": + language.Main.VideoControls.PlayCurrent = reader.Value; + break; + case "Main/VideoControls/Next": + language.Main.VideoControls.Next = reader.Value; + break; + case "Main/VideoControls/Playing": + language.Main.VideoControls.Playing = reader.Value; + break; + case "Main/VideoControls/RepeatingLastTime": + language.Main.VideoControls.RepeatingLastTime = reader.Value; + break; + case "Main/VideoControls/RepeatingXTimesLeft": + language.Main.VideoControls.RepeatingXTimesLeft = reader.Value; + break; + case "Main/VideoControls/AutoContinueInOneSecond": + language.Main.VideoControls.AutoContinueInOneSecond = reader.Value; + break; + case "Main/VideoControls/AutoContinueInXSeconds": + language.Main.VideoControls.AutoContinueInXSeconds = reader.Value; + break; + case "Main/VideoControls/StillTypingAutoContinueStopped": + language.Main.VideoControls.StillTypingAutoContinueStopped = reader.Value; + break; + case "Main/VideoControls/InsertNewSubtitleAtVideoPosition": + language.Main.VideoControls.InsertNewSubtitleAtVideoPosition = reader.Value; + break; + case "Main/VideoControls/Auto": + language.Main.VideoControls.Auto = reader.Value; + break; + case "Main/VideoControls/PlayFromJustBeforeText": + language.Main.VideoControls.PlayFromJustBeforeText = reader.Value; + break; + case "Main/VideoControls/Pause": + language.Main.VideoControls.Pause = reader.Value; + break; + case "Main/VideoControls/GoToSubtitlePositionAndPause": + language.Main.VideoControls.GoToSubtitlePositionAndPause = reader.Value; + break; + case "Main/VideoControls/SetStartTime": + language.Main.VideoControls.SetStartTime = reader.Value; + break; + case "Main/VideoControls/SetEndTimeAndGoToNext": + language.Main.VideoControls.SetEndTimeAndGoToNext = reader.Value; + break; + case "Main/VideoControls/AdjustedViaEndTime": + language.Main.VideoControls.AdjustedViaEndTime = reader.Value; + break; + case "Main/VideoControls/SetEndTime": + language.Main.VideoControls.SetEndTime = reader.Value; + break; + case "Main/VideoControls/SetstartTimeAndOffsetOfRest": + language.Main.VideoControls.SetstartTimeAndOffsetOfRest = reader.Value; + break; + case "Main/VideoControls/SearchTextOnline": + language.Main.VideoControls.SearchTextOnline = reader.Value; + break; + case "Main/VideoControls/GoogleTranslate": + language.Main.VideoControls.GoogleTranslate = reader.Value; + break; + case "Main/VideoControls/GoogleIt": + language.Main.VideoControls.GoogleIt = reader.Value; + break; + case "Main/VideoControls/SecondsBackShort": + language.Main.VideoControls.SecondsBackShort = reader.Value; + break; + case "Main/VideoControls/SecondsForwardShort": + language.Main.VideoControls.SecondsForwardShort = reader.Value; + break; + case "Main/VideoControls/VideoPosition": + language.Main.VideoControls.VideoPosition = reader.Value; + break; + case "Main/VideoControls/TranslateTip": + language.Main.VideoControls.TranslateTip = reader.Value; + break; + case "Main/VideoControls/CreateTip": + language.Main.VideoControls.CreateTip = reader.Value; + break; + case "Main/VideoControls/AdjustTip": + language.Main.VideoControls.AdjustTip = reader.Value; + break; + case "Main/VideoControls/BeforeChangingTimeInWaveformX": + language.Main.VideoControls.BeforeChangingTimeInWaveformX = reader.Value; + break; + case "Main/VideoControls/NewTextInsertAtX": + language.Main.VideoControls.NewTextInsertAtX = reader.Value; + break; + case "Main/VideoControls/Center": + language.Main.VideoControls.Center = reader.Value; + break; + case "Main/VideoControls/PlayRate": + language.Main.VideoControls.PlayRate = reader.Value; + break; + case "Main/VideoControls/Slow": + language.Main.VideoControls.Slow = reader.Value; + break; + case "Main/VideoControls/Normal": + language.Main.VideoControls.Normal = reader.Value; + break; + case "Main/VideoControls/Fast": + language.Main.VideoControls.Fast = reader.Value; + break; + case "Main/VideoControls/VeryFast": + language.Main.VideoControls.VeryFast = reader.Value; + break; + case "MatroskaSubtitleChooser/Title": + language.MatroskaSubtitleChooser.Title = reader.Value; + break; + case "MatroskaSubtitleChooser/PleaseChoose": + language.MatroskaSubtitleChooser.PleaseChoose = reader.Value; + break; + case "MatroskaSubtitleChooser/TrackXLanguageYTypeZ": + language.MatroskaSubtitleChooser.TrackXLanguageYTypeZ = reader.Value; + break; + case "MeasurementConverter/Title": + language.MeasurementConverter.Title = reader.Value; + break; + case "MeasurementConverter/ConvertFrom": + language.MeasurementConverter.ConvertFrom = reader.Value; + break; + case "MeasurementConverter/ConvertTo": + language.MeasurementConverter.ConvertTo = reader.Value; + break; + case "MeasurementConverter/CopyToClipboard": + language.MeasurementConverter.CopyToClipboard = reader.Value; + break; + case "MeasurementConverter/Celsius": + language.MeasurementConverter.Celsius = reader.Value; + break; + case "MeasurementConverter/Fahrenheit": + language.MeasurementConverter.Fahrenheit = reader.Value; + break; + case "MeasurementConverter/Miles": + language.MeasurementConverter.Miles = reader.Value; + break; + case "MeasurementConverter/Kilometers": + language.MeasurementConverter.Kilometers = reader.Value; + break; + case "MeasurementConverter/Meters": + language.MeasurementConverter.Meters = reader.Value; + break; + case "MeasurementConverter/Yards": + language.MeasurementConverter.Yards = reader.Value; + break; + case "MeasurementConverter/Feet": + language.MeasurementConverter.Feet = reader.Value; + break; + case "MeasurementConverter/Inches": + language.MeasurementConverter.Inches = reader.Value; + break; + case "MeasurementConverter/Pounds": + language.MeasurementConverter.Pounds = reader.Value; + break; + case "MeasurementConverter/Kilos": + language.MeasurementConverter.Kilos = reader.Value; + break; + case "MergeDoubleLines/Title": + language.MergeDoubleLines.Title = reader.Value; + break; + case "MergeDoubleLines/MaxMillisecondsBetweenLines": + language.MergeDoubleLines.MaxMillisecondsBetweenLines = reader.Value; + break; + case "MergeDoubleLines/IncludeIncrementing": + language.MergeDoubleLines.IncludeIncrementing = reader.Value; + break; + case "MergedShortLines/Title": + language.MergedShortLines.Title = reader.Value; + break; + case "MergedShortLines/MaximumCharacters": + language.MergedShortLines.MaximumCharacters = reader.Value; + break; + case "MergedShortLines/MaximumMillisecondsBetween": + language.MergedShortLines.MaximumMillisecondsBetween = reader.Value; + break; + case "MergedShortLines/NumberOfMergesX": + language.MergedShortLines.NumberOfMergesX = reader.Value; + break; + case "MergedShortLines/MergedText": + language.MergedShortLines.MergedText = reader.Value; + break; + case "MergedShortLines/OnlyMergeContinuationLines": + language.MergedShortLines.OnlyMergeContinuationLines = reader.Value; + break; + case "MergeTextWithSameTimeCodes/Title": + language.MergeTextWithSameTimeCodes.Title = reader.Value; + break; + case "MergeTextWithSameTimeCodes/MaxDifferenceMilliseconds": + language.MergeTextWithSameTimeCodes.MaxDifferenceMilliseconds = reader.Value; + break; + case "MergeTextWithSameTimeCodes/ReBreakLines": + language.MergeTextWithSameTimeCodes.ReBreakLines = reader.Value; + break; + case "MergeTextWithSameTimeCodes/NumberOfMergesX": + language.MergeTextWithSameTimeCodes.NumberOfMergesX = reader.Value; + break; + case "MergeTextWithSameTimeCodes/MergedText": + language.MergeTextWithSameTimeCodes.MergedText = reader.Value; + break; + case "ModifySelection/Title": + language.ModifySelection.Title = reader.Value; + break; + case "ModifySelection/Rule": + language.ModifySelection.Rule = reader.Value; + break; + case "ModifySelection/CaseSensitive": + language.ModifySelection.CaseSensitive = reader.Value; + break; + case "ModifySelection/DoWithMatches": + language.ModifySelection.DoWithMatches = reader.Value; + break; + case "ModifySelection/MakeNewSelection": + language.ModifySelection.MakeNewSelection = reader.Value; + break; + case "ModifySelection/AddToCurrentSelection": + language.ModifySelection.AddToCurrentSelection = reader.Value; + break; + case "ModifySelection/SubtractFromCurrentSelection": + language.ModifySelection.SubtractFromCurrentSelection = reader.Value; + break; + case "ModifySelection/IntersectWithCurrentSelection": + language.ModifySelection.IntersectWithCurrentSelection = reader.Value; + break; + case "ModifySelection/MatchingLinesX": + language.ModifySelection.MatchingLinesX = reader.Value; + break; + case "ModifySelection/Contains": + language.ModifySelection.Contains = reader.Value; + break; + case "ModifySelection/StartsWith": + language.ModifySelection.StartsWith = reader.Value; + break; + case "ModifySelection/EndsWith": + language.ModifySelection.EndsWith = reader.Value; + break; + case "ModifySelection/NoContains": + language.ModifySelection.NoContains = reader.Value; + break; + case "ModifySelection/RegEx": + language.ModifySelection.RegEx = reader.Value; + break; + case "ModifySelection/UnequalLines": + language.ModifySelection.UnequalLines = reader.Value; + break; + case "ModifySelection/EqualLines": + language.ModifySelection.EqualLines = reader.Value; + break; + case "MultipleReplace/Title": + language.MultipleReplace.Title = reader.Value; + break; + case "MultipleReplace/FindWhat": + language.MultipleReplace.FindWhat = reader.Value; + break; + case "MultipleReplace/ReplaceWith": + language.MultipleReplace.ReplaceWith = reader.Value; + break; + case "MultipleReplace/Normal": + language.MultipleReplace.Normal = reader.Value; + break; + case "MultipleReplace/CaseSensitive": + language.MultipleReplace.CaseSensitive = reader.Value; + break; + case "MultipleReplace/RegularExpression": + language.MultipleReplace.RegularExpression = reader.Value; + break; + case "MultipleReplace/LinesFoundX": + language.MultipleReplace.LinesFoundX = reader.Value; + break; + case "MultipleReplace/Delete": + language.MultipleReplace.Delete = reader.Value; + break; + case "MultipleReplace/Add": + language.MultipleReplace.Add = reader.Value; + break; + case "MultipleReplace/Update": + language.MultipleReplace.Update = reader.Value; + break; + case "MultipleReplace/Enabled": + language.MultipleReplace.Enabled = reader.Value; + break; + case "MultipleReplace/SearchType": + language.MultipleReplace.SearchType = reader.Value; + break; + case "MultipleReplace/RemoveAll": + language.MultipleReplace.RemoveAll = reader.Value; + break; + case "MultipleReplace/Import": + language.MultipleReplace.Import = reader.Value; + break; + case "MultipleReplace/Export": + language.MultipleReplace.Export = reader.Value; + break; + case "MultipleReplace/ImportRulesTitle": + language.MultipleReplace.ImportRulesTitle = reader.Value; + break; + case "MultipleReplace/ExportRulesTitle": + language.MultipleReplace.ExportRulesTitle = reader.Value; + break; + case "MultipleReplace/Rules": + language.MultipleReplace.Rules = reader.Value; + break; + case "MultipleReplace/MoveToTop": + language.MultipleReplace.MoveToTop = reader.Value; + break; + case "MultipleReplace/MoveToBottom": + language.MultipleReplace.MoveToBottom = reader.Value; + break; + case "NetworkChat/Title": + language.NetworkChat.Title = reader.Value; + break; + case "NetworkChat/Send": + language.NetworkChat.Send = reader.Value; + break; + case "NetworkJoin/Title": + language.NetworkJoin.Title = reader.Value; + break; + case "NetworkJoin/Information": + language.NetworkJoin.Information = reader.Value; + break; + case "NetworkJoin/Join": + language.NetworkJoin.Join = reader.Value; + break; + case "NetworkLogAndInfo/Title": + language.NetworkLogAndInfo.Title = reader.Value; + break; + case "NetworkLogAndInfo/Log": + language.NetworkLogAndInfo.Log = reader.Value; + break; + case "NetworkStart/Title": + language.NetworkStart.Title = reader.Value; + break; + case "NetworkStart/ConnectionTo": + language.NetworkStart.ConnectionTo = reader.Value; + break; + case "NetworkStart/Information": + language.NetworkStart.Information = reader.Value; + break; + case "NetworkStart/Start": + language.NetworkStart.Start = reader.Value; + break; + case "OpenVideoDvd/Title": + language.OpenVideoDvd.Title = reader.Value; + break; + case "OpenVideoDvd/OpenDvdFrom": + language.OpenVideoDvd.OpenDvdFrom = reader.Value; + break; + case "OpenVideoDvd/Disc": + language.OpenVideoDvd.Disc = reader.Value; + break; + case "OpenVideoDvd/Folder": + language.OpenVideoDvd.Folder = reader.Value; + break; + case "OpenVideoDvd/ChooseDrive": + language.OpenVideoDvd.ChooseDrive = reader.Value; + break; + case "OpenVideoDvd/ChooseFolder": + language.OpenVideoDvd.ChooseFolder = reader.Value; + break; + case "PluginsGet/Title": + language.PluginsGet.Title = reader.Value; + break; + case "PluginsGet/InstalledPlugins": + language.PluginsGet.InstalledPlugins = reader.Value; + break; + case "PluginsGet/GetPlugins": + language.PluginsGet.GetPlugins = reader.Value; + break; + case "PluginsGet/Description": + language.PluginsGet.Description = reader.Value; + break; + case "PluginsGet/Version": + language.PluginsGet.Version = reader.Value; + break; + case "PluginsGet/Date": + language.PluginsGet.Date = reader.Value; + break; + case "PluginsGet/Type": + language.PluginsGet.Type = reader.Value; + break; + case "PluginsGet/OpenPluginsFolder": + language.PluginsGet.OpenPluginsFolder = reader.Value; + break; + case "PluginsGet/GetPluginsInfo1": + language.PluginsGet.GetPluginsInfo1 = reader.Value; + break; + case "PluginsGet/GetPluginsInfo2": + language.PluginsGet.GetPluginsInfo2 = reader.Value; + break; + case "PluginsGet/PluginXDownloaded": + language.PluginsGet.PluginXDownloaded = reader.Value; + break; + case "PluginsGet/Download": + language.PluginsGet.Download = reader.Value; + break; + case "PluginsGet/Remove": + language.PluginsGet.Remove = reader.Value; + break; + case "PluginsGet/UpdateAllX": + language.PluginsGet.UpdateAllX = reader.Value; + break; + case "PluginsGet/UnableToDownloadPluginListX": + language.PluginsGet.UnableToDownloadPluginListX = reader.Value; + break; + case "PluginsGet/NewVersionOfSubtitleEditRequired": + language.PluginsGet.NewVersionOfSubtitleEditRequired = reader.Value; + break; + case "PluginsGet/UpdateAvailable": + language.PluginsGet.UpdateAvailable = reader.Value; + break; + case "PluginsGet/UpdateAll": + language.PluginsGet.UpdateAll = reader.Value; + break; + case "PluginsGet/XPluginsUpdated": + language.PluginsGet.XPluginsUpdated = reader.Value; + break; + case "RegularExpressionContextMenu/WordBoundary": + language.RegularExpressionContextMenu.WordBoundary = reader.Value; + break; + case "RegularExpressionContextMenu/NonWordBoundary": + language.RegularExpressionContextMenu.NonWordBoundary = reader.Value; + break; + case "RegularExpressionContextMenu/NewLine": + language.RegularExpressionContextMenu.NewLine = reader.Value; + break; + case "RegularExpressionContextMenu/NewLineShort": + language.RegularExpressionContextMenu.NewLineShort = reader.Value; + break; + case "RegularExpressionContextMenu/AnyDigit": + language.RegularExpressionContextMenu.AnyDigit = reader.Value; + break; + case "RegularExpressionContextMenu/NonDigit": + language.RegularExpressionContextMenu.NonDigit = reader.Value; + break; + case "RegularExpressionContextMenu/AnyCharacter": + language.RegularExpressionContextMenu.AnyCharacter = reader.Value; + break; + case "RegularExpressionContextMenu/AnyWhitespace": + language.RegularExpressionContextMenu.AnyWhitespace = reader.Value; + break; + case "RegularExpressionContextMenu/NonSpaceCharacter": + language.RegularExpressionContextMenu.NonSpaceCharacter = reader.Value; + break; + case "RegularExpressionContextMenu/ZeroOrMore": + language.RegularExpressionContextMenu.ZeroOrMore = reader.Value; + break; + case "RegularExpressionContextMenu/OneOrMore": + language.RegularExpressionContextMenu.OneOrMore = reader.Value; + break; + case "RegularExpressionContextMenu/InCharacterGroup": + language.RegularExpressionContextMenu.InCharacterGroup = reader.Value; + break; + case "RegularExpressionContextMenu/NotInCharacterGroup": + language.RegularExpressionContextMenu.NotInCharacterGroup = reader.Value; + break; + case "RemoveTextFromHearImpaired/Title": + language.RemoveTextFromHearImpaired.Title = reader.Value; + break; + case "RemoveTextFromHearImpaired/RemoveTextConditions": + language.RemoveTextFromHearImpaired.RemoveTextConditions = reader.Value; + break; + case "RemoveTextFromHearImpaired/RemoveTextBetween": + language.RemoveTextFromHearImpaired.RemoveTextBetween = reader.Value; + break; + case "RemoveTextFromHearImpaired/SquareBrackets": + language.RemoveTextFromHearImpaired.SquareBrackets = reader.Value; + break; + case "RemoveTextFromHearImpaired/Brackets": + language.RemoveTextFromHearImpaired.Brackets = reader.Value; + break; + case "RemoveTextFromHearImpaired/Parentheses": + language.RemoveTextFromHearImpaired.Parentheses = reader.Value; + break; + case "RemoveTextFromHearImpaired/QuestionMarks": + language.RemoveTextFromHearImpaired.QuestionMarks = reader.Value; + break; + case "RemoveTextFromHearImpaired/And": + language.RemoveTextFromHearImpaired.And = reader.Value; + break; + case "RemoveTextFromHearImpaired/RemoveTextBeforeColon": + language.RemoveTextFromHearImpaired.RemoveTextBeforeColon = reader.Value; + break; + case "RemoveTextFromHearImpaired/OnlyIfTextIsUppercase": + language.RemoveTextFromHearImpaired.OnlyIfTextIsUppercase = reader.Value; + break; + case "RemoveTextFromHearImpaired/OnlyIfInSeparateLine": + language.RemoveTextFromHearImpaired.OnlyIfInSeparateLine = reader.Value; + break; + case "RemoveTextFromHearImpaired/LinesFoundX": + language.RemoveTextFromHearImpaired.LinesFoundX = reader.Value; + break; + case "RemoveTextFromHearImpaired/RemoveTextIfContains": + language.RemoveTextFromHearImpaired.RemoveTextIfContains = reader.Value; + break; + case "RemoveTextFromHearImpaired/RemoveTextIfAllUppercase": + language.RemoveTextFromHearImpaired.RemoveTextIfAllUppercase = reader.Value; + break; + case "RemoveTextFromHearImpaired/RemoveInterjections": + language.RemoveTextFromHearImpaired.RemoveInterjections = reader.Value; + break; + case "RemoveTextFromHearImpaired/EditInterjections": + language.RemoveTextFromHearImpaired.EditInterjections = reader.Value; + break; + case "ReplaceDialog/Title": + language.ReplaceDialog.Title = reader.Value; + break; + case "ReplaceDialog/FindWhat": + language.ReplaceDialog.FindWhat = reader.Value; + break; + case "ReplaceDialog/Normal": + language.ReplaceDialog.Normal = reader.Value; + break; + case "ReplaceDialog/CaseSensitive": + language.ReplaceDialog.CaseSensitive = reader.Value; + break; + case "ReplaceDialog/RegularExpression": + language.ReplaceDialog.RegularExpression = reader.Value; + break; + case "ReplaceDialog/ReplaceWith": + language.ReplaceDialog.ReplaceWith = reader.Value; + break; + case "ReplaceDialog/Find": + language.ReplaceDialog.Find = reader.Value; + break; + case "ReplaceDialog/Replace": + language.ReplaceDialog.Replace = reader.Value; + break; + case "ReplaceDialog/ReplaceAll": + language.ReplaceDialog.ReplaceAll = reader.Value; + break; + case "RestoreAutoBackup/Title": + language.RestoreAutoBackup.Title = reader.Value; + break; + case "RestoreAutoBackup/Information": + language.RestoreAutoBackup.Information = reader.Value; + break; + case "RestoreAutoBackup/DateAndTime": + language.RestoreAutoBackup.DateAndTime = reader.Value; + break; + case "RestoreAutoBackup/FileName": + language.RestoreAutoBackup.FileName = reader.Value; + break; + case "RestoreAutoBackup/Extension": + language.RestoreAutoBackup.Extension = reader.Value; + break; + case "RestoreAutoBackup/NoBackedUpFilesFound": + language.RestoreAutoBackup.NoBackedUpFilesFound = reader.Value; + break; + case "SeekSilence/Title": + language.SeekSilence.Title = reader.Value; + break; + case "SeekSilence/SearchDirection": + language.SeekSilence.SearchDirection = reader.Value; + break; + case "SeekSilence/Forward": + language.SeekSilence.Forward = reader.Value; + break; + case "SeekSilence/Back": + language.SeekSilence.Back = reader.Value; + break; + case "SeekSilence/LengthInSeconds": + language.SeekSilence.LengthInSeconds = reader.Value; + break; + case "SeekSilence/MaxVolume": + language.SeekSilence.MaxVolume = reader.Value; + break; + case "SetMinimumDisplayTimeBetweenParagraphs/Title": + language.SetMinimumDisplayTimeBetweenParagraphs.Title = reader.Value; + break; + case "SetMinimumDisplayTimeBetweenParagraphs/PreviewLinesModifiedX": + language.SetMinimumDisplayTimeBetweenParagraphs.PreviewLinesModifiedX = reader.Value; + break; + case "SetMinimumDisplayTimeBetweenParagraphs/ShowOnlyModifiedLines": + language.SetMinimumDisplayTimeBetweenParagraphs.ShowOnlyModifiedLines = reader.Value; + break; + case "SetMinimumDisplayTimeBetweenParagraphs/MinimumMillisecondsBetweenParagraphs": + language.SetMinimumDisplayTimeBetweenParagraphs.MinimumMillisecondsBetweenParagraphs = reader.Value; + break; + case "SetMinimumDisplayTimeBetweenParagraphs/FrameInfo": + language.SetMinimumDisplayTimeBetweenParagraphs.FrameInfo = reader.Value; + break; + case "SetMinimumDisplayTimeBetweenParagraphs/OneFrameXisYMilliseconds": + language.SetMinimumDisplayTimeBetweenParagraphs.OneFrameXisYMilliseconds = reader.Value; + break; + case "SetSyncPoint/Title": + language.SetSyncPoint.Title = reader.Value; + break; + case "SetSyncPoint/SyncPointTimeCode": + language.SetSyncPoint.SyncPointTimeCode = reader.Value; + break; + case "SetSyncPoint/ThreeSecondsBack": + language.SetSyncPoint.ThreeSecondsBack = reader.Value; + break; + case "SetSyncPoint/HalfASecondBack": + language.SetSyncPoint.HalfASecondBack = reader.Value; + break; + case "SetSyncPoint/HalfASecondForward": + language.SetSyncPoint.HalfASecondForward = reader.Value; + break; + case "SetSyncPoint/ThreeSecondsForward": + language.SetSyncPoint.ThreeSecondsForward = reader.Value; + break; + case "Settings/Title": + language.Settings.Title = reader.Value; + break; + case "Settings/General": + language.Settings.General = reader.Value; + break; + case "Settings/Toolbar": + language.Settings.Toolbar = reader.Value; + break; + case "Settings/VideoPlayer": + language.Settings.VideoPlayer = reader.Value; + break; + case "Settings/WaveformAndSpectrogram": + language.Settings.WaveformAndSpectrogram = reader.Value; + break; + case "Settings/Tools": + language.Settings.Tools = reader.Value; + break; + case "Settings/WordLists": + language.Settings.WordLists = reader.Value; + break; + case "Settings/SsaStyle": + language.Settings.SsaStyle = reader.Value; + break; + case "Settings/Network": + language.Settings.Network = reader.Value; + break; + case "Settings/ShowToolBarButtons": + language.Settings.ShowToolBarButtons = reader.Value; + break; + case "Settings/New": + language.Settings.New = reader.Value; + break; + case "Settings/Open": + language.Settings.Open = reader.Value; + break; + case "Settings/Save": + language.Settings.Save = reader.Value; + break; + case "Settings/SaveAs": + language.Settings.SaveAs = reader.Value; + break; + case "Settings/Find": + language.Settings.Find = reader.Value; + break; + case "Settings/Replace": + language.Settings.Replace = reader.Value; + break; + case "Settings/VisualSync": + language.Settings.VisualSync = reader.Value; + break; + case "Settings/SpellCheck": + language.Settings.SpellCheck = reader.Value; + break; + case "Settings/SettingsName": + language.Settings.SettingsName = reader.Value; + break; + case "Settings/Help": + language.Settings.Help = reader.Value; + break; + case "Settings/ShowFrameRate": + language.Settings.ShowFrameRate = reader.Value; + break; + case "Settings/DefaultFrameRate": + language.Settings.DefaultFrameRate = reader.Value; + break; + case "Settings/DefaultFileEncoding": + language.Settings.DefaultFileEncoding = reader.Value; + break; + case "Settings/AutoDetectAnsiEncoding": + language.Settings.AutoDetectAnsiEncoding = reader.Value; + break; + case "Settings/SubtitleLineMaximumLength": + language.Settings.SubtitleLineMaximumLength = reader.Value; + break; + case "Settings/MaximumCharactersPerSecond": + language.Settings.MaximumCharactersPerSecond = reader.Value; + break; + case "Settings/AutoWrapWhileTyping": + language.Settings.AutoWrapWhileTyping = reader.Value; + break; + case "Settings/DurationMinimumMilliseconds": + language.Settings.DurationMinimumMilliseconds = reader.Value; + break; + case "Settings/DurationMaximumMilliseconds": + language.Settings.DurationMaximumMilliseconds = reader.Value; + break; + case "Settings/MinimumGapMilliseconds": + language.Settings.MinimumGapMilliseconds = reader.Value; + break; + case "Settings/SubtitleFont": + language.Settings.SubtitleFont = reader.Value; + break; + case "Settings/SubtitleFontSize": + language.Settings.SubtitleFontSize = reader.Value; + break; + case "Settings/SubtitleBold": + language.Settings.SubtitleBold = reader.Value; + break; + case "Settings/SubtitleCenter": + language.Settings.SubtitleCenter = reader.Value; + break; + case "Settings/SubtitleFontColor": + language.Settings.SubtitleFontColor = reader.Value; + break; + case "Settings/SubtitleBackgroundColor": + language.Settings.SubtitleBackgroundColor = reader.Value; + break; + case "Settings/SpellChecker": + language.Settings.SpellChecker = reader.Value; + break; + case "Settings/RememberRecentFiles": + language.Settings.RememberRecentFiles = reader.Value; + break; + case "Settings/StartWithLastFileLoaded": + language.Settings.StartWithLastFileLoaded = reader.Value; + break; + case "Settings/RememberSelectedLine": + language.Settings.RememberSelectedLine = reader.Value; + break; + case "Settings/RememberPositionAndSize": + language.Settings.RememberPositionAndSize = reader.Value; + break; + case "Settings/StartInSourceView": + language.Settings.StartInSourceView = reader.Value; + break; + case "Settings/RemoveBlankLinesWhenOpening": + language.Settings.RemoveBlankLinesWhenOpening = reader.Value; + break; + case "Settings/ShowLineBreaksAs": + language.Settings.ShowLineBreaksAs = reader.Value; + break; + case "Settings/MainListViewDoubleClickAction": + language.Settings.MainListViewDoubleClickAction = reader.Value; + break; + case "Settings/MainListViewNothing": + language.Settings.MainListViewNothing = reader.Value; + break; + case "Settings/MainListViewVideoGoToPositionAndPause": + language.Settings.MainListViewVideoGoToPositionAndPause = reader.Value; + break; + case "Settings/MainListViewVideoGoToPositionAndPlay": + language.Settings.MainListViewVideoGoToPositionAndPlay = reader.Value; + break; + case "Settings/MainListViewEditText": + language.Settings.MainListViewEditText = reader.Value; + break; + case "Settings/MainListViewVideoGoToPositionMinus1SecAndPause": + language.Settings.MainListViewVideoGoToPositionMinus1SecAndPause = reader.Value; + break; + case "Settings/MainListViewVideoGoToPositionMinusHalfSecAndPause": + language.Settings.MainListViewVideoGoToPositionMinusHalfSecAndPause = reader.Value; + break; + case "Settings/MainListViewVideoGoToPositionMinus1SecAndPlay": + language.Settings.MainListViewVideoGoToPositionMinus1SecAndPlay = reader.Value; + break; + case "Settings/MainListViewEditTextAndPause": + language.Settings.MainListViewEditTextAndPause = reader.Value; + break; + case "Settings/AutoBackup": + language.Settings.AutoBackup = reader.Value; + break; + case "Settings/AutoBackupEveryMinute": + language.Settings.AutoBackupEveryMinute = reader.Value; + break; + case "Settings/AutoBackupEveryFiveMinutes": + language.Settings.AutoBackupEveryFiveMinutes = reader.Value; + break; + case "Settings/AutoBackupEveryFifteenMinutes": + language.Settings.AutoBackupEveryFifteenMinutes = reader.Value; + break; + case "Settings/CheckForUpdates": + language.Settings.CheckForUpdates = reader.Value; + break; + case "Settings/AllowEditOfOriginalSubtitle": + language.Settings.AllowEditOfOriginalSubtitle = reader.Value; + break; + case "Settings/PromptDeleteLines": + language.Settings.PromptDeleteLines = reader.Value; + break; + case "Settings/TimeCodeMode": + language.Settings.TimeCodeMode = reader.Value; + break; + case "Settings/TimeCodeModeHHMMSSMS": + language.Settings.TimeCodeModeHHMMSSMS = reader.Value; + break; + case "Settings/TimeCodeModeHHMMSSFF": + language.Settings.TimeCodeModeHHMMSSFF = reader.Value; + break; + case "Settings/VideoEngine": + language.Settings.VideoEngine = reader.Value; + break; + case "Settings/DirectShow": + language.Settings.DirectShow = reader.Value; + break; + case "Settings/DirectShowDescription": + language.Settings.DirectShowDescription = reader.Value; + break; + case "Settings/ManagedDirectX": + language.Settings.ManagedDirectX = reader.Value; + break; + case "Settings/ManagedDirectXDescription": + language.Settings.ManagedDirectXDescription = reader.Value; + break; + case "Settings/MpcHc": + language.Settings.MpcHc = reader.Value; + break; + case "Settings/MpcHcDescription": + language.Settings.MpcHcDescription = reader.Value; + break; + case "Settings/MPlayer": + language.Settings.MPlayer = reader.Value; + break; + case "Settings/MPlayerDescription": + language.Settings.MPlayerDescription = reader.Value; + break; + case "Settings/VlcMediaPlayer": + language.Settings.VlcMediaPlayer = reader.Value; + break; + case "Settings/VlcMediaPlayerDescription": + language.Settings.VlcMediaPlayerDescription = reader.Value; + break; + case "Settings/VlcBrowseToLabel": + language.Settings.VlcBrowseToLabel = reader.Value; + break; + case "Settings/ShowStopButton": + language.Settings.ShowStopButton = reader.Value; + break; + case "Settings/ShowMuteButton": + language.Settings.ShowMuteButton = reader.Value; + break; + case "Settings/ShowFullscreenButton": + language.Settings.ShowFullscreenButton = reader.Value; + break; + case "Settings/PreviewFontSize": + language.Settings.PreviewFontSize = reader.Value; + break; + case "Settings/MainWindowVideoControls": + language.Settings.MainWindowVideoControls = reader.Value; + break; + case "Settings/CustomSearchTextAndUrl": + language.Settings.CustomSearchTextAndUrl = reader.Value; + break; + case "Settings/WaveformAppearance": + language.Settings.WaveformAppearance = reader.Value; + break; + case "Settings/WaveformGridColor": + language.Settings.WaveformGridColor = reader.Value; + break; + case "Settings/WaveformShowGridLines": + language.Settings.WaveformShowGridLines = reader.Value; + break; + case "Settings/ReverseMouseWheelScrollDirection": + language.Settings.ReverseMouseWheelScrollDirection = reader.Value; + break; + case "Settings/WaveformAllowOverlap": + language.Settings.WaveformAllowOverlap = reader.Value; + break; + case "Settings/WaveformFocusMouseEnter": + language.Settings.WaveformFocusMouseEnter = reader.Value; + break; + case "Settings/WaveformListViewFocusMouseEnter": + language.Settings.WaveformListViewFocusMouseEnter = reader.Value; + break; + case "Settings/WaveformBorderHitMs1": + language.Settings.WaveformBorderHitMs1 = reader.Value; + break; + case "Settings/WaveformBorderHitMs2": + language.Settings.WaveformBorderHitMs2 = reader.Value; + break; + case "Settings/WaveformColor": + language.Settings.WaveformColor = reader.Value; + break; + case "Settings/WaveformSelectedColor": + language.Settings.WaveformSelectedColor = reader.Value; + break; + case "Settings/WaveformBackgroundColor": + language.Settings.WaveformBackgroundColor = reader.Value; + break; + case "Settings/WaveformTextColor": + language.Settings.WaveformTextColor = reader.Value; + break; + case "Settings/WaveformTextFontSize": + language.Settings.WaveformTextFontSize = reader.Value; + break; + case "Settings/WaveformAndSpectrogramsFolderEmpty": + language.Settings.WaveformAndSpectrogramsFolderEmpty = reader.Value; + break; + case "Settings/WaveformAndSpectrogramsFolderInfo": + language.Settings.WaveformAndSpectrogramsFolderInfo = reader.Value; + break; + case "Settings/Spectrogram": + language.Settings.Spectrogram = reader.Value; + break; + case "Settings/GenerateSpectrogram": + language.Settings.GenerateSpectrogram = reader.Value; + break; + case "Settings/SpectrogramAppearance": + language.Settings.SpectrogramAppearance = reader.Value; + break; + case "Settings/SpectrogramOneColorGradient": + language.Settings.SpectrogramOneColorGradient = reader.Value; + break; + case "Settings/SpectrogramClassic": + language.Settings.SpectrogramClassic = reader.Value; + break; + case "Settings/WaveformUseFFmpeg": + language.Settings.WaveformUseFFmpeg = reader.Value; + break; + case "Settings/WaveformFFmpegPath": + language.Settings.WaveformFFmpegPath = reader.Value; + break; + case "Settings/WaveformBrowseToFFmpeg": + language.Settings.WaveformBrowseToFFmpeg = reader.Value; + break; + case "Settings/WaveformBrowseToVLC": + language.Settings.WaveformBrowseToVLC = reader.Value; + break; + case "Settings/SubStationAlphaStyle": + language.Settings.SubStationAlphaStyle = reader.Value; + break; + case "Settings/ChooseFont": + language.Settings.ChooseFont = reader.Value; + break; + case "Settings/ChooseColor": + language.Settings.ChooseColor = reader.Value; + break; + case "Settings/SsaOutline": + language.Settings.SsaOutline = reader.Value; + break; + case "Settings/SsaShadow": + language.Settings.SsaShadow = reader.Value; + break; + case "Settings/SsaOpaqueBox": + language.Settings.SsaOpaqueBox = reader.Value; + break; + case "Settings/Testing123": + language.Settings.Testing123 = reader.Value; + break; + case "Settings/Language": + language.Settings.Language = reader.Value; + break; + case "Settings/NamesIgnoreLists": + language.Settings.NamesIgnoreLists = reader.Value; + break; + case "Settings/AddNameEtc": + language.Settings.AddNameEtc = reader.Value; + break; + case "Settings/AddWord": + language.Settings.AddWord = reader.Value; + break; + case "Settings/Remove": + language.Settings.Remove = reader.Value; + break; + case "Settings/AddPair": + language.Settings.AddPair = reader.Value; + break; + case "Settings/UserWordList": + language.Settings.UserWordList = reader.Value; + break; + case "Settings/OcrFixList": + language.Settings.OcrFixList = reader.Value; + break; + case "Settings/Location": + language.Settings.Location = reader.Value; + break; + case "Settings/UseOnlineNamesEtc": + language.Settings.UseOnlineNamesEtc = reader.Value; + break; + case "Settings/WordAddedX": + language.Settings.WordAddedX = reader.Value; + break; + case "Settings/WordAlreadyExists": + language.Settings.WordAlreadyExists = reader.Value; + break; + case "Settings/WordNotFound": + language.Settings.WordNotFound = reader.Value; + break; + case "Settings/RemoveX": + language.Settings.RemoveX = reader.Value; + break; + case "Settings/CannotUpdateNamesEtcOnline": + language.Settings.CannotUpdateNamesEtcOnline = reader.Value; + break; + case "Settings/ProxyServerSettings": + language.Settings.ProxyServerSettings = reader.Value; + break; + case "Settings/ProxyAddress": + language.Settings.ProxyAddress = reader.Value; + break; + case "Settings/ProxyAuthentication": + language.Settings.ProxyAuthentication = reader.Value; + break; + case "Settings/ProxyUserName": + language.Settings.ProxyUserName = reader.Value; + break; + case "Settings/ProxyPassword": + language.Settings.ProxyPassword = reader.Value; + break; + case "Settings/ProxyDomain": + language.Settings.ProxyDomain = reader.Value; + break; + case "Settings/NetworkSessionSettings": + language.Settings.NetworkSessionSettings = reader.Value; + break; + case "Settings/NetworkSessionNewSound": + language.Settings.NetworkSessionNewSound = reader.Value; + break; + case "Settings/PlayXSecondsAndBack": + language.Settings.PlayXSecondsAndBack = reader.Value; + break; + case "Settings/StartSceneIndex": + language.Settings.StartSceneIndex = reader.Value; + break; + case "Settings/EndSceneIndex": + language.Settings.EndSceneIndex = reader.Value; + break; + case "Settings/FirstPlusX": + language.Settings.FirstPlusX = reader.Value; + break; + case "Settings/LastMinusX": + language.Settings.LastMinusX = reader.Value; + break; + case "Settings/FixCommonerrors": + language.Settings.FixCommonerrors = reader.Value; + break; + case "Settings/MergeLinesShorterThan": + language.Settings.MergeLinesShorterThan = reader.Value; + break; + case "Settings/MusicSymbol": + language.Settings.MusicSymbol = reader.Value; + break; + case "Settings/MusicSymbolsToReplace": + language.Settings.MusicSymbolsToReplace = reader.Value; + break; + case "Settings/FixCommonOcrErrorsUseHardcodedRules": + language.Settings.FixCommonOcrErrorsUseHardcodedRules = reader.Value; + break; + case "Settings/FixCommonerrorsFixShortDisplayTimesAllowMoveStartTime": + language.Settings.FixCommonerrorsFixShortDisplayTimesAllowMoveStartTime = reader.Value; + break; + case "Settings/Shortcuts": + language.Settings.Shortcuts = reader.Value; + break; + case "Settings/Shortcut": + language.Settings.Shortcut = reader.Value; + break; + case "Settings/Control": + language.Settings.Control = reader.Value; + break; + case "Settings/Alt": + language.Settings.Alt = reader.Value; + break; + case "Settings/Shift": + language.Settings.Shift = reader.Value; + break; + case "Settings/Key": + language.Settings.Key = reader.Value; + break; + case "Settings/TextBox": + language.Settings.TextBox = reader.Value; + break; + case "Settings/UpdateShortcut": + language.Settings.UpdateShortcut = reader.Value; + break; + case "Settings/ShortcutIsNotValid": + language.Settings.ShortcutIsNotValid = reader.Value; + break; + case "Settings/ToggleDockUndockOfVideoControls": + language.Settings.ToggleDockUndockOfVideoControls = reader.Value; + break; + case "Settings/CreateSetEndAddNewAndGoToNew": + language.Settings.CreateSetEndAddNewAndGoToNew = reader.Value; + break; + case "Settings/AdjustViaEndAutoStartAndGoToNext": + language.Settings.AdjustViaEndAutoStartAndGoToNext = reader.Value; + break; + case "Settings/AdjustSetEndTimeAndGoToNext": + language.Settings.AdjustSetEndTimeAndGoToNext = reader.Value; + break; + case "Settings/AdjustSetStartAutoDurationAndGoToNext": + language.Settings.AdjustSetStartAutoDurationAndGoToNext = reader.Value; + break; + case "Settings/AdjustSetEndNextStartAndGoToNext": + language.Settings.AdjustSetEndNextStartAndGoToNext = reader.Value; + break; + case "Settings/AdjustStartDownEndUpAndGoToNext": + language.Settings.AdjustStartDownEndUpAndGoToNext = reader.Value; + break; + case "Settings/AdjustSelected100MsForward": + language.Settings.AdjustSelected100MsForward = reader.Value; + break; + case "Settings/AdjustSelected100MsBack": + language.Settings.AdjustSelected100MsBack = reader.Value; + break; + case "Settings/AdjustSetStartTimeKeepDuration": + language.Settings.AdjustSetStartTimeKeepDuration = reader.Value; + break; + case "Settings/AdjustSetEndAndOffsetTheRest": + language.Settings.AdjustSetEndAndOffsetTheRest = reader.Value; + break; + case "Settings/AdjustSetEndAndOffsetTheRestAndGoToNext": + language.Settings.AdjustSetEndAndOffsetTheRestAndGoToNext = reader.Value; + break; + case "Settings/MainCreateStartDownEndUp": + language.Settings.MainCreateStartDownEndUp = reader.Value; + break; + case "Settings/MergeDialog": + language.Settings.MergeDialog = reader.Value; + break; + case "Settings/GoToNext": + language.Settings.GoToNext = reader.Value; + break; + case "Settings/GoToPrevious": + language.Settings.GoToPrevious = reader.Value; + break; + case "Settings/GoToCurrentSubtitleStart": + language.Settings.GoToCurrentSubtitleStart = reader.Value; + break; + case "Settings/GoToCurrentSubtitleEnd": + language.Settings.GoToCurrentSubtitleEnd = reader.Value; + break; + case "Settings/ToggleFocus": + language.Settings.ToggleFocus = reader.Value; + break; + case "Settings/ToggleDialogDashes": + language.Settings.ToggleDialogDashes = reader.Value; + break; + case "Settings/Alignment": + language.Settings.Alignment = reader.Value; + break; + case "Settings/CopyTextOnly": + language.Settings.CopyTextOnly = reader.Value; + break; + case "Settings/CopyTextOnlyFromOriginalToCurrent": + language.Settings.CopyTextOnlyFromOriginalToCurrent = reader.Value; + break; + case "Settings/AutoDurationSelectedLines": + language.Settings.AutoDurationSelectedLines = reader.Value; + break; + case "Settings/ReverseStartAndEndingForRTL": + language.Settings.ReverseStartAndEndingForRTL = reader.Value; + break; + case "Settings/VerticalZoom": + language.Settings.VerticalZoom = reader.Value; + break; + case "Settings/VerticalZoomOut": + language.Settings.VerticalZoomOut = reader.Value; + break; + case "Settings/WaveformSeekSilenceForward": + language.Settings.WaveformSeekSilenceForward = reader.Value; + break; + case "Settings/WaveformSeekSilenceBack": + language.Settings.WaveformSeekSilenceBack = reader.Value; + break; + case "Settings/WaveformAddTextHere": + language.Settings.WaveformAddTextHere = reader.Value; + break; + case "Settings/WaveformPlayNewSelection": + language.Settings.WaveformPlayNewSelection = reader.Value; + break; + case "Settings/WaveformPlayFirstSelectedSubtitle": + language.Settings.WaveformPlayFirstSelectedSubtitle = reader.Value; + break; + case "Settings/WaveformFocusListView": + language.Settings.WaveformFocusListView = reader.Value; + break; + case "Settings/GoBack1Frame": + language.Settings.GoBack1Frame = reader.Value; + break; + case "Settings/GoForward1Frame": + language.Settings.GoForward1Frame = reader.Value; + break; + case "Settings/GoBack100Milliseconds": + language.Settings.GoBack100Milliseconds = reader.Value; + break; + case "Settings/GoForward100Milliseconds": + language.Settings.GoForward100Milliseconds = reader.Value; + break; + case "Settings/GoBack500Milliseconds": + language.Settings.GoBack500Milliseconds = reader.Value; + break; + case "Settings/GoForward500Milliseconds": + language.Settings.GoForward500Milliseconds = reader.Value; + break; + case "Settings/GoBack1Second": + language.Settings.GoBack1Second = reader.Value; + break; + case "Settings/GoForward1Second": + language.Settings.GoForward1Second = reader.Value; + break; + case "Settings/TogglePlayPause": + language.Settings.TogglePlayPause = reader.Value; + break; + case "Settings/Pause": + language.Settings.Pause = reader.Value; + break; + case "Settings/Fullscreen": + language.Settings.Fullscreen = reader.Value; + break; + case "Settings/CustomSearch1": + language.Settings.CustomSearch1 = reader.Value; + break; + case "Settings/CustomSearch2": + language.Settings.CustomSearch2 = reader.Value; + break; + case "Settings/CustomSearch3": + language.Settings.CustomSearch3 = reader.Value; + break; + case "Settings/CustomSearch4": + language.Settings.CustomSearch4 = reader.Value; + break; + case "Settings/CustomSearch5": + language.Settings.CustomSearch5 = reader.Value; + break; + case "Settings/CustomSearch6": + language.Settings.CustomSearch6 = reader.Value; + break; + case "Settings/SyntaxColoring": + language.Settings.SyntaxColoring = reader.Value; + break; + case "Settings/ListViewSyntaxColoring": + language.Settings.ListViewSyntaxColoring = reader.Value; + break; + case "Settings/SyntaxColorDurationIfTooSmall": + language.Settings.SyntaxColorDurationIfTooSmall = reader.Value; + break; + case "Settings/SyntaxColorDurationIfTooLarge": + language.Settings.SyntaxColorDurationIfTooLarge = reader.Value; + break; + case "Settings/SyntaxColorTextIfTooLong": + language.Settings.SyntaxColorTextIfTooLong = reader.Value; + break; + case "Settings/SyntaxColorTextMoreThanXLines": + language.Settings.SyntaxColorTextMoreThanXLines = reader.Value; + break; + case "Settings/SyntaxColorOverlap": + language.Settings.SyntaxColorOverlap = reader.Value; + break; + case "Settings/SyntaxErrorColor": + language.Settings.SyntaxErrorColor = reader.Value; + break; + case "Settings/GoToFirstSelectedLine": + language.Settings.GoToFirstSelectedLine = reader.Value; + break; + case "Settings/GoToNextEmptyLine": + language.Settings.GoToNextEmptyLine = reader.Value; + break; + case "Settings/MergeSelectedLines": + language.Settings.MergeSelectedLines = reader.Value; + break; + case "Settings/MergeSelectedLinesOnlyFirstText": + language.Settings.MergeSelectedLinesOnlyFirstText = reader.Value; + break; + case "Settings/ToggleTranslationMode": + language.Settings.ToggleTranslationMode = reader.Value; + break; + case "Settings/SwitchOriginalAndTranslation": + language.Settings.SwitchOriginalAndTranslation = reader.Value; + break; + case "Settings/MergeOriginalAndTranslation": + language.Settings.MergeOriginalAndTranslation = reader.Value; + break; + case "Settings/ShortcutIsAlreadyDefinedX": + language.Settings.ShortcutIsAlreadyDefinedX = reader.Value; + break; + case "Settings/ToggleTranslationAndOriginalInPreviews": + language.Settings.ToggleTranslationAndOriginalInPreviews = reader.Value; + break; + case "Settings/ListViewColumnDelete": + language.Settings.ListViewColumnDelete = reader.Value; + break; + case "Settings/ListViewColumnInsert": + language.Settings.ListViewColumnInsert = reader.Value; + break; + case "Settings/ListViewColumnPaste": + language.Settings.ListViewColumnPaste = reader.Value; + break; + case "Settings/ListViewFocusWaveform": + language.Settings.ListViewFocusWaveform = reader.Value; + break; + case "Settings/ListViewGoToNextError": + language.Settings.ListViewGoToNextError = reader.Value; + break; + case "Settings/ShowBeamer": + language.Settings.ShowBeamer = reader.Value; + break; + case "Settings/MainTextBoxMoveLastWordDown": + language.Settings.MainTextBoxMoveLastWordDown = reader.Value; + break; + case "Settings/MainTextBoxMoveFirstWordFromNextUp": + language.Settings.MainTextBoxMoveFirstWordFromNextUp = reader.Value; + break; + case "Settings/MainTextBoxSelectionToLower": + language.Settings.MainTextBoxSelectionToLower = reader.Value; + break; + case "Settings/MainTextBoxSelectionToUpper": + language.Settings.MainTextBoxSelectionToUpper = reader.Value; + break; + case "Settings/MainTextBoxToggleAutoDuration": + language.Settings.MainTextBoxToggleAutoDuration = reader.Value; + break; + case "Settings/MainTextBoxAutoBreak": + language.Settings.MainTextBoxAutoBreak = reader.Value; + break; + case "Settings/MainTextBoxUnbreak": + language.Settings.MainTextBoxUnbreak = reader.Value; + break; + case "Settings/MainFileSaveAll": + language.Settings.MainFileSaveAll = reader.Value; + break; + case "Settings/Miscellaneous": + language.Settings.Miscellaneous = reader.Value; + break; + case "Settings/UseDoNotBreakAfterList": + language.Settings.UseDoNotBreakAfterList = reader.Value; + break; + case "SetVideoOffset/Title": + language.SetVideoOffset.Title = reader.Value; + break; + case "SetVideoOffset/Description": + language.SetVideoOffset.Description = reader.Value; + break; + case "SetVideoOffset/RelativeToCurrentVideoPosition": + language.SetVideoOffset.RelativeToCurrentVideoPosition = reader.Value; + break; + case "ShowEarlierLater/Title": + language.ShowEarlierLater.Title = reader.Value; + break; + case "ShowEarlierLater/TitleAll": + language.ShowEarlierLater.TitleAll = reader.Value; + break; + case "ShowEarlierLater/ShowEarlier": + language.ShowEarlierLater.ShowEarlier = reader.Value; + break; + case "ShowEarlierLater/ShowLater": + language.ShowEarlierLater.ShowLater = reader.Value; + break; + case "ShowEarlierLater/TotalAdjustmentX": + language.ShowEarlierLater.TotalAdjustmentX = reader.Value; + break; + case "ShowEarlierLater/AllLines": + language.ShowEarlierLater.AllLines = reader.Value; + break; + case "ShowEarlierLater/SelectedLinesOnly": + language.ShowEarlierLater.SelectedLinesOnly = reader.Value; + break; + case "ShowEarlierLater/SelectedLinesAndForward": + language.ShowEarlierLater.SelectedLinesAndForward = reader.Value; + break; + case "ShowHistory/Title": + language.ShowHistory.Title = reader.Value; + break; + case "ShowHistory/SelectRollbackPoint": + language.ShowHistory.SelectRollbackPoint = reader.Value; + break; + case "ShowHistory/Time": + language.ShowHistory.Time = reader.Value; + break; + case "ShowHistory/Description": + language.ShowHistory.Description = reader.Value; + break; + case "ShowHistory/CompareHistoryItems": + language.ShowHistory.CompareHistoryItems = reader.Value; + break; + case "ShowHistory/CompareWithCurrent": + language.ShowHistory.CompareWithCurrent = reader.Value; + break; + case "ShowHistory/Rollback": + language.ShowHistory.Rollback = reader.Value; + break; + case "SpellCheck/Title": + language.SpellCheck.Title = reader.Value; + break; + case "SpellCheck/FullText": + language.SpellCheck.FullText = reader.Value; + break; + case "SpellCheck/WordNotFound": + language.SpellCheck.WordNotFound = reader.Value; + break; + case "SpellCheck/Language": + language.SpellCheck.Language = reader.Value; + break; + case "SpellCheck/Change": + language.SpellCheck.Change = reader.Value; + break; + case "SpellCheck/ChangeAll": + language.SpellCheck.ChangeAll = reader.Value; + break; + case "SpellCheck/SkipOnce": + language.SpellCheck.SkipOnce = reader.Value; + break; + case "SpellCheck/SkipAll": + language.SpellCheck.SkipAll = reader.Value; + break; + case "SpellCheck/AddToUserDictionary": + language.SpellCheck.AddToUserDictionary = reader.Value; + break; + case "SpellCheck/AddToNamesAndIgnoreList": + language.SpellCheck.AddToNamesAndIgnoreList = reader.Value; + break; + case "SpellCheck/AddToOcrReplaceList": + language.SpellCheck.AddToOcrReplaceList = reader.Value; + break; + case "SpellCheck/Abort": + language.SpellCheck.Abort = reader.Value; + break; + case "SpellCheck/Use": + language.SpellCheck.Use = reader.Value; + break; + case "SpellCheck/UseAlways": + language.SpellCheck.UseAlways = reader.Value; + break; + case "SpellCheck/Suggestions": + language.SpellCheck.Suggestions = reader.Value; + break; + case "SpellCheck/SpellCheckProgress": + language.SpellCheck.SpellCheckProgress = reader.Value; + break; + case "SpellCheck/EditWholeText": + language.SpellCheck.EditWholeText = reader.Value; + break; + case "SpellCheck/EditWordOnly": + language.SpellCheck.EditWordOnly = reader.Value; + break; + case "SpellCheck/AddXToNamesEtc": + language.SpellCheck.AddXToNamesEtc = reader.Value; + break; + case "SpellCheck/AutoFixNames": + language.SpellCheck.AutoFixNames = reader.Value; + break; + case "SpellCheck/CheckOneLetterWords": + language.SpellCheck.CheckOneLetterWords = reader.Value; + break; + case "SpellCheck/TreatINQuoteAsING": + language.SpellCheck.TreatINQuoteAsING = reader.Value; + break; + case "SpellCheck/ImageText": + language.SpellCheck.ImageText = reader.Value; + break; + case "SpellCheck/SpellCheckCompleted": + language.SpellCheck.SpellCheckCompleted = reader.Value; + break; + case "SpellCheck/SpellCheckAborted": + language.SpellCheck.SpellCheckAborted = reader.Value; + break; + case "SpellCheck/UndoX": + language.SpellCheck.UndoX = reader.Value; + break; + case "Split/Title": + language.Split.Title = reader.Value; + break; + case "Split/SplitOptions": + language.Split.SplitOptions = reader.Value; + break; + case "Split/Lines": + language.Split.Lines = reader.Value; + break; + case "Split/Characters": + language.Split.Characters = reader.Value; + break; + case "Split/NumberOfEqualParts": + language.Split.NumberOfEqualParts = reader.Value; + break; + case "Split/SubtitleInfo": + language.Split.SubtitleInfo = reader.Value; + break; + case "Split/NumberOfLinesX": + language.Split.NumberOfLinesX = reader.Value; + break; + case "Split/NumberOfCharactersX": + language.Split.NumberOfCharactersX = reader.Value; + break; + case "Split/Output": + language.Split.Output = reader.Value; + break; + case "Split/FileName": + language.Split.FileName = reader.Value; + break; + case "Split/OutputFolder": + language.Split.OutputFolder = reader.Value; + break; + case "Split/DoSplit": + language.Split.DoSplit = reader.Value; + break; + case "Split/Basic": + language.Split.Basic = reader.Value; + break; + case "SplitLongLines/Title": + language.SplitLongLines.Title = reader.Value; + break; + case "SplitLongLines/SingleLineMaximumLength": + language.SplitLongLines.SingleLineMaximumLength = reader.Value; + break; + case "SplitLongLines/LineMaximumLength": + language.SplitLongLines.LineMaximumLength = reader.Value; + break; + case "SplitLongLines/LineContinuationBeginEndStrings": + language.SplitLongLines.LineContinuationBeginEndStrings = reader.Value; + break; + case "SplitLongLines/NumberOfSplits": + language.SplitLongLines.NumberOfSplits = reader.Value; + break; + case "SplitLongLines/LongestSingleLineIsXAtY": + language.SplitLongLines.LongestSingleLineIsXAtY = reader.Value; + break; + case "SplitLongLines/LongestLineIsXAtY": + language.SplitLongLines.LongestLineIsXAtY = reader.Value; + break; + case "SplitSubtitle/Title": + language.SplitSubtitle.Title = reader.Value; + break; + case "SplitSubtitle/Description1": + language.SplitSubtitle.Description1 = reader.Value; + break; + case "SplitSubtitle/Description2": + language.SplitSubtitle.Description2 = reader.Value; + break; + case "SplitSubtitle/Split": + language.SplitSubtitle.Split = reader.Value; + break; + case "SplitSubtitle/Done": + language.SplitSubtitle.Done = reader.Value; + break; + case "SplitSubtitle/NothingToSplit": + language.SplitSubtitle.NothingToSplit = reader.Value; + break; + case "SplitSubtitle/SavePartOneAs": + language.SplitSubtitle.SavePartOneAs = reader.Value; + break; + case "SplitSubtitle/SavePartTwoAs": + language.SplitSubtitle.SavePartTwoAs = reader.Value; + break; + case "SplitSubtitle/Part1": + language.SplitSubtitle.Part1 = reader.Value; + break; + case "SplitSubtitle/Part2": + language.SplitSubtitle.Part2 = reader.Value; + break; + case "SplitSubtitle/UnableToSaveFileX": + language.SplitSubtitle.UnableToSaveFileX = reader.Value; + break; + case "SplitSubtitle/OverwriteExistingFiles": + language.SplitSubtitle.OverwriteExistingFiles = reader.Value; + break; + case "SplitSubtitle/FolderNotFoundX": + language.SplitSubtitle.FolderNotFoundX = reader.Value; + break; + case "SplitSubtitle/Untitled": + language.SplitSubtitle.Untitled = reader.Value; + break; + case "StartNumberingFrom/Title": + language.StartNumberingFrom.Title = reader.Value; + break; + case "StartNumberingFrom/StartFromNumber": + language.StartNumberingFrom.StartFromNumber = reader.Value; + break; + case "StartNumberingFrom/PleaseEnterAValidNumber": + language.StartNumberingFrom.PleaseEnterAValidNumber = reader.Value; + break; + case "Statistics/Title": + language.Statistics.Title = reader.Value; + break; + case "Statistics/TitleWithFileName": + language.Statistics.TitleWithFileName = reader.Value; + break; + case "Statistics/GeneralStatistics": + language.Statistics.GeneralStatistics = reader.Value; + break; + case "Statistics/MostUsed": + language.Statistics.MostUsed = reader.Value; + break; + case "Statistics/MostUsedLines": + language.Statistics.MostUsedLines = reader.Value; + break; + case "Statistics/MostUsedWords": + language.Statistics.MostUsedWords = reader.Value; + break; + case "Statistics/NothingFound": + language.Statistics.NothingFound = reader.Value; + break; + case "Statistics/NumberOfLinesX": + language.Statistics.NumberOfLinesX = reader.Value; + break; + case "Statistics/LengthInFormatXinCharactersY": + language.Statistics.LengthInFormatXinCharactersY = reader.Value; + break; + case "Statistics/NumberOfCharactersInTextOnly": + language.Statistics.NumberOfCharactersInTextOnly = reader.Value; + break; + case "Statistics/TotalCharsPerSecond": + language.Statistics.TotalCharsPerSecond = reader.Value; + break; + case "Statistics/NumberOfItalicTags": + language.Statistics.NumberOfItalicTags = reader.Value; + break; + case "Statistics/NumberOfBoldTags": + language.Statistics.NumberOfBoldTags = reader.Value; + break; + case "Statistics/NumberOfUnderlineTags": + language.Statistics.NumberOfUnderlineTags = reader.Value; + break; + case "Statistics/NumberOfFontTags": + language.Statistics.NumberOfFontTags = reader.Value; + break; + case "Statistics/NumberOfAlignmentTags": + language.Statistics.NumberOfAlignmentTags = reader.Value; + break; + case "Statistics/LineLengthMinimum": + language.Statistics.LineLengthMinimum = reader.Value; + break; + case "Statistics/LineLengthMaximum": + language.Statistics.LineLengthMaximum = reader.Value; + break; + case "Statistics/LineLengthAverage": + language.Statistics.LineLengthAverage = reader.Value; + break; + case "Statistics/LinesPerSubtitleAverage": + language.Statistics.LinesPerSubtitleAverage = reader.Value; + break; + case "Statistics/SingleLineLengthMinimum": + language.Statistics.SingleLineLengthMinimum = reader.Value; + break; + case "Statistics/SingleLineLengthMaximum": + language.Statistics.SingleLineLengthMaximum = reader.Value; + break; + case "Statistics/SingleLineLengthAverage": + language.Statistics.SingleLineLengthAverage = reader.Value; + break; + case "Statistics/DurationMinimum": + language.Statistics.DurationMinimum = reader.Value; + break; + case "Statistics/DurationMaximum": + language.Statistics.DurationMaximum = reader.Value; + break; + case "Statistics/DurationAverage": + language.Statistics.DurationAverage = reader.Value; + break; + case "Statistics/CharactersPerSecondMinimum": + language.Statistics.CharactersPerSecondMinimum = reader.Value; + break; + case "Statistics/CharactersPerSecondMaximum": + language.Statistics.CharactersPerSecondMaximum = reader.Value; + break; + case "Statistics/CharactersPerSecondAverage": + language.Statistics.CharactersPerSecondAverage = reader.Value; + break; + case "Statistics/Export": + language.Statistics.Export = reader.Value; + break; + case "SubStationAlphaProperties/Title": + language.SubStationAlphaProperties.Title = reader.Value; + break; + case "SubStationAlphaProperties/TitleSubstationAlpha": + language.SubStationAlphaProperties.TitleSubstationAlpha = reader.Value; + break; + case "SubStationAlphaProperties/Script": + language.SubStationAlphaProperties.Script = reader.Value; + break; + case "SubStationAlphaProperties/ScriptTitle": + language.SubStationAlphaProperties.ScriptTitle = reader.Value; + break; + case "SubStationAlphaProperties/OriginalScript": + language.SubStationAlphaProperties.OriginalScript = reader.Value; + break; + case "SubStationAlphaProperties/Translation": + language.SubStationAlphaProperties.Translation = reader.Value; + break; + case "SubStationAlphaProperties/Editing": + language.SubStationAlphaProperties.Editing = reader.Value; + break; + case "SubStationAlphaProperties/Timing": + language.SubStationAlphaProperties.Timing = reader.Value; + break; + case "SubStationAlphaProperties/SyncPoint": + language.SubStationAlphaProperties.SyncPoint = reader.Value; + break; + case "SubStationAlphaProperties/UpdatedBy": + language.SubStationAlphaProperties.UpdatedBy = reader.Value; + break; + case "SubStationAlphaProperties/UpdateDetails": + language.SubStationAlphaProperties.UpdateDetails = reader.Value; + break; + case "SubStationAlphaProperties/Resolution": + language.SubStationAlphaProperties.Resolution = reader.Value; + break; + case "SubStationAlphaProperties/VideoResolution": + language.SubStationAlphaProperties.VideoResolution = reader.Value; + break; + case "SubStationAlphaProperties/Options": + language.SubStationAlphaProperties.Options = reader.Value; + break; + case "SubStationAlphaProperties/WrapStyle": + language.SubStationAlphaProperties.WrapStyle = reader.Value; + break; + case "SubStationAlphaProperties/Collision": + language.SubStationAlphaProperties.Collision = reader.Value; + break; + case "SubStationAlphaProperties/ScaleBorderAndShadow": + language.SubStationAlphaProperties.ScaleBorderAndShadow = reader.Value; + break; + case "SubStationAlphaStyles/Title": + language.SubStationAlphaStyles.Title = reader.Value; + break; + case "SubStationAlphaStyles/TitleSubstationAlpha": + language.SubStationAlphaStyles.TitleSubstationAlpha = reader.Value; + break; + case "SubStationAlphaStyles/Styles": + language.SubStationAlphaStyles.Styles = reader.Value; + break; + case "SubStationAlphaStyles/Properties": + language.SubStationAlphaStyles.Properties = reader.Value; + break; + case "SubStationAlphaStyles/Name": + language.SubStationAlphaStyles.Name = reader.Value; + break; + case "SubStationAlphaStyles/Font": + language.SubStationAlphaStyles.Font = reader.Value; + break; + case "SubStationAlphaStyles/FontName": + language.SubStationAlphaStyles.FontName = reader.Value; + break; + case "SubStationAlphaStyles/FontSize": + language.SubStationAlphaStyles.FontSize = reader.Value; + break; + case "SubStationAlphaStyles/UseCount": + language.SubStationAlphaStyles.UseCount = reader.Value; + break; + case "SubStationAlphaStyles/Primary": + language.SubStationAlphaStyles.Primary = reader.Value; + break; + case "SubStationAlphaStyles/Secondary": + language.SubStationAlphaStyles.Secondary = reader.Value; + break; + case "SubStationAlphaStyles/Tertiary": + language.SubStationAlphaStyles.Tertiary = reader.Value; + break; + case "SubStationAlphaStyles/Outline": + language.SubStationAlphaStyles.Outline = reader.Value; + break; + case "SubStationAlphaStyles/Shadow": + language.SubStationAlphaStyles.Shadow = reader.Value; + break; + case "SubStationAlphaStyles/Back": + language.SubStationAlphaStyles.Back = reader.Value; + break; + case "SubStationAlphaStyles/Alignment": + language.SubStationAlphaStyles.Alignment = reader.Value; + break; + case "SubStationAlphaStyles/TopLeft": + language.SubStationAlphaStyles.TopLeft = reader.Value; + break; + case "SubStationAlphaStyles/TopCenter": + language.SubStationAlphaStyles.TopCenter = reader.Value; + break; + case "SubStationAlphaStyles/TopRight": + language.SubStationAlphaStyles.TopRight = reader.Value; + break; + case "SubStationAlphaStyles/MiddleLeft": + language.SubStationAlphaStyles.MiddleLeft = reader.Value; + break; + case "SubStationAlphaStyles/MiddleCenter": + language.SubStationAlphaStyles.MiddleCenter = reader.Value; + break; + case "SubStationAlphaStyles/MiddleRight": + language.SubStationAlphaStyles.MiddleRight = reader.Value; + break; + case "SubStationAlphaStyles/BottomLeft": + language.SubStationAlphaStyles.BottomLeft = reader.Value; + break; + case "SubStationAlphaStyles/BottomCenter": + language.SubStationAlphaStyles.BottomCenter = reader.Value; + break; + case "SubStationAlphaStyles/BottomRight": + language.SubStationAlphaStyles.BottomRight = reader.Value; + break; + case "SubStationAlphaStyles/Colors": + language.SubStationAlphaStyles.Colors = reader.Value; + break; + case "SubStationAlphaStyles/Margins": + language.SubStationAlphaStyles.Margins = reader.Value; + break; + case "SubStationAlphaStyles/MarginLeft": + language.SubStationAlphaStyles.MarginLeft = reader.Value; + break; + case "SubStationAlphaStyles/MarginRight": + language.SubStationAlphaStyles.MarginRight = reader.Value; + break; + case "SubStationAlphaStyles/MarginVertical": + language.SubStationAlphaStyles.MarginVertical = reader.Value; + break; + case "SubStationAlphaStyles/Border": + language.SubStationAlphaStyles.Border = reader.Value; + break; + case "SubStationAlphaStyles/PlusShadow": + language.SubStationAlphaStyles.PlusShadow = reader.Value; + break; + case "SubStationAlphaStyles/OpaqueBox": + language.SubStationAlphaStyles.OpaqueBox = reader.Value; + break; + case "SubStationAlphaStyles/Import": + language.SubStationAlphaStyles.Import = reader.Value; + break; + case "SubStationAlphaStyles/Export": + language.SubStationAlphaStyles.Export = reader.Value; + break; + case "SubStationAlphaStyles/Copy": + language.SubStationAlphaStyles.Copy = reader.Value; + break; + case "SubStationAlphaStyles/CopyOfY": + language.SubStationAlphaStyles.CopyOfY = reader.Value; + break; + case "SubStationAlphaStyles/CopyXOfY": + language.SubStationAlphaStyles.CopyXOfY = reader.Value; + break; + case "SubStationAlphaStyles/New": + language.SubStationAlphaStyles.New = reader.Value; + break; + case "SubStationAlphaStyles/Remove": + language.SubStationAlphaStyles.Remove = reader.Value; + break; + case "SubStationAlphaStyles/RemoveAll": + language.SubStationAlphaStyles.RemoveAll = reader.Value; + break; + case "SubStationAlphaStyles/ImportStyleFromFile": + language.SubStationAlphaStyles.ImportStyleFromFile = reader.Value; + break; + case "SubStationAlphaStyles/ExportStyleToFile": + language.SubStationAlphaStyles.ExportStyleToFile = reader.Value; + break; + case "SubStationAlphaStyles/ChooseStyle": + language.SubStationAlphaStyles.ChooseStyle = reader.Value; + break; + case "SubStationAlphaStyles/StyleAlreadyExits": + language.SubStationAlphaStyles.StyleAlreadyExits = reader.Value; + break; + case "SubStationAlphaStyles/StyleXExportedToFileY": + language.SubStationAlphaStyles.StyleXExportedToFileY = reader.Value; + break; + case "SubStationAlphaStyles/StyleXImportedFromFileY": + language.SubStationAlphaStyles.StyleXImportedFromFileY = reader.Value; + break; + case "PointSync/Title": + language.PointSync.Title = reader.Value; + break; + case "PointSync/TitleViaOtherSubtitle": + language.PointSync.TitleViaOtherSubtitle = reader.Value; + break; + case "PointSync/SyncHelp": + language.PointSync.SyncHelp = reader.Value; + break; + case "PointSync/SetSyncPoint": + language.PointSync.SetSyncPoint = reader.Value; + break; + case "PointSync/RemoveSyncPoint": + language.PointSync.RemoveSyncPoint = reader.Value; + break; + case "PointSync/SyncPointsX": + language.PointSync.SyncPointsX = reader.Value; + break; + case "PointSync/Info": + language.PointSync.Info = reader.Value; + break; + case "PointSync/ApplySync": + language.PointSync.ApplySync = reader.Value; + break; + case "TransportStreamSubtitleChooser/Title": + language.TransportStreamSubtitleChooser.Title = reader.Value; + break; + case "TransportStreamSubtitleChooser/PidLine": + language.TransportStreamSubtitleChooser.PidLine = reader.Value; + break; + case "TransportStreamSubtitleChooser/SubLine": + language.TransportStreamSubtitleChooser.SubLine = reader.Value; + break; + case "UnknownSubtitle/Title": + language.UnknownSubtitle.Title = reader.Value; + break; + case "UnknownSubtitle/Message": + language.UnknownSubtitle.Message = reader.Value; + break; + case "VisualSync/Title": + language.VisualSync.Title = reader.Value; + break; + case "VisualSync/StartScene": + language.VisualSync.StartScene = reader.Value; + break; + case "VisualSync/EndScene": + language.VisualSync.EndScene = reader.Value; + break; + case "VisualSync/Synchronize": + language.VisualSync.Synchronize = reader.Value; + break; + case "VisualSync/HalfASecondBack": + language.VisualSync.HalfASecondBack = reader.Value; + break; + case "VisualSync/ThreeSecondsBack": + language.VisualSync.ThreeSecondsBack = reader.Value; + break; + case "VisualSync/PlayXSecondsAndBack": + language.VisualSync.PlayXSecondsAndBack = reader.Value; + break; + case "VisualSync/FindText": + language.VisualSync.FindText = reader.Value; + break; + case "VisualSync/GoToSubPosition": + language.VisualSync.GoToSubPosition = reader.Value; + break; + case "VisualSync/KeepChangesTitle": + language.VisualSync.KeepChangesTitle = reader.Value; + break; + case "VisualSync/KeepChangesMessage": + language.VisualSync.KeepChangesMessage = reader.Value; + break; + case "VisualSync/SynchronizationDone": + language.VisualSync.SynchronizationDone = reader.Value; + break; + case "VisualSync/StartSceneMustComeBeforeEndScene": + language.VisualSync.StartSceneMustComeBeforeEndScene = reader.Value; + break; + case "VisualSync/Tip": + language.VisualSync.Tip = reader.Value; + break; + case "VobSubEditCharacters/Title": + language.VobSubEditCharacters.Title = reader.Value; + break; + case "VobSubEditCharacters/ChooseCharacter": + language.VobSubEditCharacters.ChooseCharacter = reader.Value; + break; + case "VobSubEditCharacters/ImageCompareFiles": + language.VobSubEditCharacters.ImageCompareFiles = reader.Value; + break; + case "VobSubEditCharacters/CurrentCompareImage": + language.VobSubEditCharacters.CurrentCompareImage = reader.Value; + break; + case "VobSubEditCharacters/TextAssociatedWithImage": + language.VobSubEditCharacters.TextAssociatedWithImage = reader.Value; + break; + case "VobSubEditCharacters/IsItalic": + language.VobSubEditCharacters.IsItalic = reader.Value; + break; + case "VobSubEditCharacters/Update": + language.VobSubEditCharacters.Update = reader.Value; + break; + case "VobSubEditCharacters/Delete": + language.VobSubEditCharacters.Delete = reader.Value; + break; + case "VobSubEditCharacters/ImageDoubleSize": + language.VobSubEditCharacters.ImageDoubleSize = reader.Value; + break; + case "VobSubEditCharacters/ImageFileNotFound": + language.VobSubEditCharacters.ImageFileNotFound = reader.Value; + break; + case "VobSubEditCharacters/Image": + language.VobSubEditCharacters.Image = reader.Value; + break; + case "VobSubOcr/Title": + language.VobSubOcr.Title = reader.Value; + break; + case "VobSubOcr/TitleBluRay": + language.VobSubOcr.TitleBluRay = reader.Value; + break; + case "VobSubOcr/OcrMethod": + language.VobSubOcr.OcrMethod = reader.Value; + break; + case "VobSubOcr/OcrViaModi": + language.VobSubOcr.OcrViaModi = reader.Value; + break; + case "VobSubOcr/OcrViaTesseract": + language.VobSubOcr.OcrViaTesseract = reader.Value; + break; + case "VobSubOcr/OcrViaNOCR": + language.VobSubOcr.OcrViaNOCR = reader.Value; + break; + case "VobSubOcr/Language": + language.VobSubOcr.Language = reader.Value; + break; + case "VobSubOcr/OcrViaImageCompare": + language.VobSubOcr.OcrViaImageCompare = reader.Value; + break; + case "VobSubOcr/ImageDatabase": + language.VobSubOcr.ImageDatabase = reader.Value; + break; + case "VobSubOcr/NoOfPixelsIsSpace": + language.VobSubOcr.NoOfPixelsIsSpace = reader.Value; + break; + case "VobSubOcr/MaxErrorPercent": + language.VobSubOcr.MaxErrorPercent = reader.Value; + break; + case "VobSubOcr/New": + language.VobSubOcr.New = reader.Value; + break; + case "VobSubOcr/Edit": + language.VobSubOcr.Edit = reader.Value; + break; + case "VobSubOcr/StartOcr": + language.VobSubOcr.StartOcr = reader.Value; + break; + case "VobSubOcr/Stop": + language.VobSubOcr.Stop = reader.Value; + break; + case "VobSubOcr/StartOcrFrom": + language.VobSubOcr.StartOcrFrom = reader.Value; + break; + case "VobSubOcr/LoadingVobSubImages": + language.VobSubOcr.LoadingVobSubImages = reader.Value; + break; + case "VobSubOcr/LoadingImageCompareDatabase": + language.VobSubOcr.LoadingImageCompareDatabase = reader.Value; + break; + case "VobSubOcr/ConvertingImageCompareDatabase": + language.VobSubOcr.ConvertingImageCompareDatabase = reader.Value; + break; + case "VobSubOcr/SubtitleImage": + language.VobSubOcr.SubtitleImage = reader.Value; + break; + case "VobSubOcr/SubtitleText": + language.VobSubOcr.SubtitleText = reader.Value; + break; + case "VobSubOcr/UnableToCreateCharacterDatabaseFolder": + language.VobSubOcr.UnableToCreateCharacterDatabaseFolder = reader.Value; + break; + case "VobSubOcr/SubtitleImageXofY": + language.VobSubOcr.SubtitleImageXofY = reader.Value; + break; + case "VobSubOcr/ImagePalette": + language.VobSubOcr.ImagePalette = reader.Value; + break; + case "VobSubOcr/UseCustomColors": + language.VobSubOcr.UseCustomColors = reader.Value; + break; + case "VobSubOcr/Transparent": + language.VobSubOcr.Transparent = reader.Value; + break; + case "VobSubOcr/TransparentMinAlpha": + language.VobSubOcr.TransparentMinAlpha = reader.Value; + break; + case "VobSubOcr/TransportStream": + language.VobSubOcr.TransportStream = reader.Value; + break; + case "VobSubOcr/TransportStreamGrayscale": + language.VobSubOcr.TransportStreamGrayscale = reader.Value; + break; + case "VobSubOcr/TransportStreamGetColor": + language.VobSubOcr.TransportStreamGetColor = reader.Value; + break; + case "VobSubOcr/PromptForUnknownWords": + language.VobSubOcr.PromptForUnknownWords = reader.Value; + break; + case "VobSubOcr/TryToGuessUnkownWords": + language.VobSubOcr.TryToGuessUnkownWords = reader.Value; + break; + case "VobSubOcr/AutoBreakSubtitleIfMoreThanTwoLines": + language.VobSubOcr.AutoBreakSubtitleIfMoreThanTwoLines = reader.Value; + break; + case "VobSubOcr/AllFixes": + language.VobSubOcr.AllFixes = reader.Value; + break; + case "VobSubOcr/GuessesUsed": + language.VobSubOcr.GuessesUsed = reader.Value; + break; + case "VobSubOcr/UnknownWords": + language.VobSubOcr.UnknownWords = reader.Value; + break; + case "VobSubOcr/OcrAutoCorrectionSpellChecking": + language.VobSubOcr.OcrAutoCorrectionSpellChecking = reader.Value; + break; + case "VobSubOcr/FixOcrErrors": + language.VobSubOcr.FixOcrErrors = reader.Value; + break; + case "VobSubOcr/ImportTextWithMatchingTimeCodes": + language.VobSubOcr.ImportTextWithMatchingTimeCodes = reader.Value; + break; + case "VobSubOcr/ImportNewTimeCodes": + language.VobSubOcr.ImportNewTimeCodes = reader.Value; + break; + case "VobSubOcr/SaveSubtitleImageAs": + language.VobSubOcr.SaveSubtitleImageAs = reader.Value; + break; + case "VobSubOcr/SaveAllSubtitleImagesAsBdnXml": + language.VobSubOcr.SaveAllSubtitleImagesAsBdnXml = reader.Value; + break; + case "VobSubOcr/SaveAllSubtitleImagesWithHtml": + language.VobSubOcr.SaveAllSubtitleImagesWithHtml = reader.Value; + break; + case "VobSubOcr/XImagesSavedInY": + language.VobSubOcr.XImagesSavedInY = reader.Value; + break; + case "VobSubOcr/TryModiForUnknownWords": + language.VobSubOcr.TryModiForUnknownWords = reader.Value; + break; + case "VobSubOcr/DictionaryX": + language.VobSubOcr.DictionaryX = reader.Value; + break; + case "VobSubOcr/RightToLeft": + language.VobSubOcr.RightToLeft = reader.Value; + break; + case "VobSubOcr/ShowOnlyForcedSubtitles": + language.VobSubOcr.ShowOnlyForcedSubtitles = reader.Value; + break; + case "VobSubOcr/UseTimeCodesFromIdx": + language.VobSubOcr.UseTimeCodesFromIdx = reader.Value; + break; + case "VobSubOcr/NoMatch": + language.VobSubOcr.NoMatch = reader.Value; + break; + case "VobSubOcr/AutoTransparentBackground": + language.VobSubOcr.AutoTransparentBackground = reader.Value; + break; + case "VobSubOcr/InspectCompareMatchesForCurrentImage": + language.VobSubOcr.InspectCompareMatchesForCurrentImage = reader.Value; + break; + case "VobSubOcr/EditLastAdditions": + language.VobSubOcr.EditLastAdditions = reader.Value; + break; + case "VobSubOcr/SetUnitalicFactor": + language.VobSubOcr.SetUnitalicFactor = reader.Value; + break; + case "VobSubOcr/DiscardTitle": + language.VobSubOcr.DiscardTitle = reader.Value; + break; + case "VobSubOcr/DiscardText": + language.VobSubOcr.DiscardText = reader.Value; + break; + case "VobSubOcrCharacter/Title": + language.VobSubOcrCharacter.Title = reader.Value; + break; + case "VobSubOcrCharacter/ShrinkSelection": + language.VobSubOcrCharacter.ShrinkSelection = reader.Value; + break; + case "VobSubOcrCharacter/ExpandSelection": + language.VobSubOcrCharacter.ExpandSelection = reader.Value; + break; + case "VobSubOcrCharacter/SubtitleImage": + language.VobSubOcrCharacter.SubtitleImage = reader.Value; + break; + case "VobSubOcrCharacter/Characters": + language.VobSubOcrCharacter.Characters = reader.Value; + break; + case "VobSubOcrCharacter/CharactersAsText": + language.VobSubOcrCharacter.CharactersAsText = reader.Value; + break; + case "VobSubOcrCharacter/Italic": + language.VobSubOcrCharacter.Italic = reader.Value; + break; + case "VobSubOcrCharacter/Abort": + language.VobSubOcrCharacter.Abort = reader.Value; + break; + case "VobSubOcrCharacter/Skip": + language.VobSubOcrCharacter.Skip = reader.Value; + break; + case "VobSubOcrCharacter/Nordic": + language.VobSubOcrCharacter.Nordic = reader.Value; + break; + case "VobSubOcrCharacter/Spanish": + language.VobSubOcrCharacter.Spanish = reader.Value; + break; + case "VobSubOcrCharacter/German": + language.VobSubOcrCharacter.German = reader.Value; + break; + case "VobSubOcrCharacter/AutoSubmitOnFirstChar": + language.VobSubOcrCharacter.AutoSubmitOnFirstChar = reader.Value; + break; + case "VobSubOcrCharacter/EditLastX": + language.VobSubOcrCharacter.EditLastX = reader.Value; + break; + case "VobSubOcrCharacterInspect/Title": + language.VobSubOcrCharacterInspect.Title = reader.Value; + break; + case "VobSubOcrCharacterInspect/InspectItems": + language.VobSubOcrCharacterInspect.InspectItems = reader.Value; + break; + case "VobSubOcrCharacterInspect/AddBetterMatch": + language.VobSubOcrCharacterInspect.AddBetterMatch = reader.Value; + break; + case "VobSubOcrNewFolder/Title": + language.VobSubOcrNewFolder.Title = reader.Value; + break; + case "VobSubOcrNewFolder/Message": + language.VobSubOcrNewFolder.Message = reader.Value; + break; + case "VobSubOcrSetItalicFactor/Title": + language.VobSubOcrSetItalicFactor.Title = reader.Value; + break; + case "VobSubOcrSetItalicFactor/Description": + language.VobSubOcrSetItalicFactor.Description = reader.Value; + break; + case "Waveform/ClickToAddWaveform": + language.Waveform.ClickToAddWaveform = reader.Value; + break; + case "Waveform/ClickToAddWaveformAndSpectrogram": + language.Waveform.ClickToAddWaveformAndSpectrogram = reader.Value; + break; + case "Waveform/Seconds": + language.Waveform.Seconds = reader.Value; + break; + case "Waveform/ZoomIn": + language.Waveform.ZoomIn = reader.Value; + break; + case "Waveform/ZoomOut": + language.Waveform.ZoomOut = reader.Value; + break; + case "Waveform/AddParagraphHere": + language.Waveform.AddParagraphHere = reader.Value; + break; + case "Waveform/AddParagraphHereAndPasteText": + language.Waveform.AddParagraphHereAndPasteText = reader.Value; + break; + case "Waveform/FocusTextBox": + language.Waveform.FocusTextBox = reader.Value; + break; + case "Waveform/DeleteParagraph": + language.Waveform.DeleteParagraph = reader.Value; + break; + case "Waveform/Split": + language.Waveform.Split = reader.Value; + break; + case "Waveform/SplitAtCursor": + language.Waveform.SplitAtCursor = reader.Value; + break; + case "Waveform/MergeWithPrevious": + language.Waveform.MergeWithPrevious = reader.Value; + break; + case "Waveform/MergeWithNext": + language.Waveform.MergeWithNext = reader.Value; + break; + case "Waveform/PlaySelection": + language.Waveform.PlaySelection = reader.Value; + break; + case "Waveform/ShowWaveformAndSpectrogram": + language.Waveform.ShowWaveformAndSpectrogram = reader.Value; + break; + case "Waveform/ShowWaveformOnly": + language.Waveform.ShowWaveformOnly = reader.Value; + break; + case "Waveform/ShowSpectrogramOnly": + language.Waveform.ShowSpectrogramOnly = reader.Value; + break; + case "Waveform/GuessTimeCodes": + language.Waveform.GuessTimeCodes = reader.Value; + break; + case "Waveform/SeekSilence": + language.Waveform.SeekSilence = reader.Value; + break; + case "WaveformGenerateTimeCodes/Title": + language.WaveformGenerateTimeCodes.Title = reader.Value; + break; + case "WaveformGenerateTimeCodes/StartFrom": + language.WaveformGenerateTimeCodes.StartFrom = reader.Value; + break; + case "WaveformGenerateTimeCodes/CurrentVideoPosition": + language.WaveformGenerateTimeCodes.CurrentVideoPosition = reader.Value; + break; + case "WaveformGenerateTimeCodes/Beginning": + language.WaveformGenerateTimeCodes.Beginning = reader.Value; + break; + case "WaveformGenerateTimeCodes/DeleteLines": + language.WaveformGenerateTimeCodes.DeleteLines = reader.Value; + break; + case "WaveformGenerateTimeCodes/FromCurrentVideoPosition": + language.WaveformGenerateTimeCodes.FromCurrentVideoPosition = reader.Value; + break; + case "WaveformGenerateTimeCodes/DetectOptions": + language.WaveformGenerateTimeCodes.DetectOptions = reader.Value; + break; + case "WaveformGenerateTimeCodes/ScanBlocksOfMs": + language.WaveformGenerateTimeCodes.ScanBlocksOfMs = reader.Value; + break; + case "WaveformGenerateTimeCodes/BlockAverageVolMin1": + language.WaveformGenerateTimeCodes.BlockAverageVolMin1 = reader.Value; + break; + case "WaveformGenerateTimeCodes/BlockAverageVolMin2": + language.WaveformGenerateTimeCodes.BlockAverageVolMin2 = reader.Value; + break; + case "WaveformGenerateTimeCodes/BlockAverageVolMax1": + language.WaveformGenerateTimeCodes.BlockAverageVolMax1 = reader.Value; + break; + case "WaveformGenerateTimeCodes/BlockAverageVolMax2": + language.WaveformGenerateTimeCodes.BlockAverageVolMax2 = reader.Value; + break; + case "WaveformGenerateTimeCodes/SplitLongLinesAt1": + language.WaveformGenerateTimeCodes.SplitLongLinesAt1 = reader.Value; + break; + case "WaveformGenerateTimeCodes/SplitLongLinesAt2": + language.WaveformGenerateTimeCodes.SplitLongLinesAt2 = reader.Value; + break; + case "WaveformGenerateTimeCodes/Other": + language.WaveformGenerateTimeCodes.Other = reader.Value; + break; + case "WebVttNewVoice/Title": + language.WebVttNewVoice.Title = reader.Value; + break; + case "WebVttNewVoice/VoiceName": + language.WebVttNewVoice.VoiceName = reader.Value; + break; + + } + } + } +} diff --git a/libse/LanguageStructure.cs b/libse/LanguageStructure.cs new file mode 100644 index 000000000..56f09b898 --- /dev/null +++ b/libse/LanguageStructure.cs @@ -0,0 +1,2326 @@ +namespace Nikse.SubtitleEdit.Core +{ + // The language classes are built for easy xml-serialization (makes save/load code simple) + public class LanguageStructure + { + public class General + { + public string Title { get; set; } + public string Version { get; set; } + public string TranslatedBy { get; set; } + public string CultureName { get; set; } + public string HelpFile { get; set; } + public string Ok { get; set; } + public string Cancel { get; set; } + public string Apply { get; set; } + public string None { get; set; } + public string All { get; set; } + public string Preview { get; set; } + public string SubtitleFiles { get; set; } + public string AllFiles { get; set; } + public string VideoFiles { get; set; } + public string AudioFiles { get; set; } + public string OpenSubtitle { get; set; } + public string OpenVideoFile { get; set; } + public string OpenVideoFileTitle { get; set; } + public string NoVideoLoaded { get; set; } + public string VideoInformation { get; set; } + public string PositionX { get; set; } + public string StartTime { get; set; } + public string EndTime { get; set; } + public string Duration { get; set; } + public string NumberSymbol { get; set; } + public string Number { get; set; } + public string Text { get; set; } + public string HourMinutesSecondsMilliseconds { get; set; } + public string Bold { get; set; } + public string Italic { get; set; } + public string Underline { get; set; } + public string Visible { get; set; } + public string FrameRate { get; set; } + public string Name { get; set; } + public string FileNameXAndSize { get; set; } + public string ResolutionX { get; set; } + public string FrameRateX { get; set; } + public string TotalFramesX { get; set; } + public string VideoEncodingX { get; set; } + public string SingleLineLengths { get; set; } + public string TotalLengthX { get; set; } + public string TotalLengthXSplitLine { get; set; } + public string SplitLine { get; set; } + public string NotAvailable { get; set; } + public string OverlapPreviousLineX { get; set; } + public string OverlapX { get; set; } + public string OverlapNextX { get; set; } + public string Negative { get; set; } + public string RegularExpressionIsNotValid { get; set; } + public string SubtitleSaved { get; set; } + public string CurrentSubtitle { get; set; } + public string OriginalText { get; set; } + public string OpenOriginalSubtitleFile { get; set; } + public string PleaseWait { get; set; } + public string SessionKey { get; set; } + public string UserName { get; set; } + public string UserNameAlreadyInUse { get; set; } + public string WebServiceUrl { get; set; } + public string IP { get; set; } + public string VideoWindowTitle { get; set; } + public string AudioWindowTitle { get; set; } + public string ControlsWindowTitle { get; set; } + public string Advanced { get; set; } + public string Style { get; set; } + public string StyleLanguage { get; set; } + public string Character { get; set; } + public string Class { get; set; } + public string GeneralText { get; set; } + public string LineNumber { get; set; } + public string Before { get; set; } + public string After { get; set; } + public string Size { get; set; } + } + + public class About + { + public string Title { get; set; } + public string AboutText1 { get; set; } + } + + public class AddToNames + { + public string Title { get; set; } + public string Description { get; set; } + } + + public class AddToOcrReplaceList + { + public string Title { get; set; } + public string Description { get; set; } + } + + public class AddToUserDictionary + { + public string Title { get; set; } + public string Description { get; set; } + } + + public class AddWaveform + { + public string Title { get; set; } + public string SourceVideoFile { get; set; } + public string GenerateWaveformData { get; set; } + public string PleaseWait { get; set; } + public string VlcMediaPlayerNotFoundTitle { get; set; } + public string VlcMediaPlayerNotFound { get; set; } + public string GoToVlcMediaPlayerHomePage { get; set; } + public string GeneratingPeakFile { get; set; } + public string GeneratingSpectrogram { get; set; } + public string ExtractingSeconds { get; set; } + public string ExtractingMinutes { get; set; } + public string WaveFileNotFound { get; set; } + public string WaveFileMalformed { get; set; } + public string LowDiskSpace { get; set; } + public string FreeDiskSpace { get; set; } + } + + public class AddWaveformBatch + { + public string Title { get; set; } + public string ExtractingAudio { get; set; } + public string Calculating { get; set; } + public string Done { get; set; } + public string Error { get; set; } + } + + public class AdjustDisplayDuration + { + public string Title { get; set; } + public string AdjustVia { get; set; } + public string Seconds { get; set; } + public string Percent { get; set; } + public string Recalculate { get; set; } + public string AddSeconds { get; set; } + public string SetAsPercent { get; set; } + public string Note { get; set; } + public string PleaseSelectAValueFromTheDropDownList { get; set; } + public string PleaseChoose { get; set; } + } + + public class ApplyDurationLimits + { + public string Title { get; set; } + public string FixesAvailable { get; set; } + public string UnableToFix { get; set; } + } + + public class AutoBreakUnbreakLines + { + public string TitleAutoBreak { get; set; } + public string TitleUnbreak { get; set; } + public string LinesFoundX { get; set; } + public string OnlyBreakLinesLongerThan { get; set; } + public string OnlyUnbreakLinesLongerThan { get; set; } + } + + public class BatchConvert + { + public string Title { get; set; } + public string Input { get; set; } + public string InputDescription { get; set; } + public string Status { get; set; } + public string Output { get; set; } + public string ChooseOutputFolder { get; set; } + public string OverwriteExistingFiles { get; set; } + public string Style { get; set; } + public string ConvertOptions { get; set; } + public string RemoveFormatting { get; set; } + public string RemoveTextForHI { get; set; } + public string OverwriteOriginalFiles { get; set; } + public string RedoCasing { get; set; } + public string Convert { get; set; } + public string NothingToConvert { get; set; } + public string PleaseChooseOutputFolder { get; set; } + public string NotConverted { get; set; } + public string Converted { get; set; } + public string ConvertedX { get; set; } + public string Settings { get; set; } + public string SplitLongLines { get; set; } + public string AutoBalance { get; set; } + public string ScanFolder { get; set; } + public string ScanningFolder { get; set; } + public string Recursive { get; set; } + public string SetMinMsBetweenSubtitles { get; set; } + public string PlainText { get; set; } + public string Ocr { get; set; } + public string Filter { get; set; } + public string FilterSkipped { get; set; } + public string FilterSrtNoUtf8BOM { get; set; } + public string FilterMoreThanTwoLines { get; set; } + public string FilterContains { get; set; } + public string FixCommonErrorsErrorX { get; set; } + public string MultipleReplaceErrorX { get; set; } + public string AutoBalanceErrorX { get; set; } + } + + public class Beamer + { + public string Title { get; set; } + } + + public class ChangeCasing + { + public string Title { get; set; } + public string ChangeCasingTo { get; set; } + public string NormalCasing { get; set; } + public string FixNamesCasing { get; set; } + public string FixOnlyNamesCasing { get; set; } + public string OnlyChangeAllUppercaseLines { get; set; } + public string AllUppercase { get; set; } + public string AllLowercase { get; set; } + } + + public class ChangeCasingNames + { + public string Title { get; set; } + public string NamesFoundInSubtitleX { get; set; } + public string Enabled { get; set; } + public string Name { get; set; } + public string LinesFoundX { get; set; } + } + + public class ChangeFrameRate + { + public string Title { get; set; } + public string ConvertFrameRateOfSubtitle { get; set; } + public string FromFrameRate { get; set; } + public string ToFrameRate { get; set; } + public string FrameRateNotCorrect { get; set; } + public string FrameRateNotChanged { get; set; } + } + + public class ChangeSpeedInPercent + { + public string Title { get; set; } + public string Info { get; set; } + public string Custom { get; set; } + public string ToDropFrame { get; set; } + public string FromDropFrame { get; set; } + } + + public class CheckForUpdates + { + public string Title { get; set; } + public string CheckingForUpdates { get; set; } + public string CheckingForUpdatesFailedX { get; set; } + public string CheckingForUpdatesNoneAvailable { get; set; } + public string CheckingForUpdatesNewVersion { get; set; } + public string InstallUpdate { get; set; } + public string NoUpdates { get; set; } + } + + public class ChooseAudioTrack + { + public string Title { get; set; } + } + + public class ChooseEncoding + { + public string Title { get; set; } + public string CodePage { get; set; } + public string DisplayName { get; set; } + public string PleaseSelectAnEncoding { get; set; } + } + + public class ChooseLanguage + { + public string Title { get; set; } + public string Language { get; set; } + } + + public class ColorChooser + { + public string Title { get; set; } + public string Red { get; set; } + public string Green { get; set; } + public string Blue { get; set; } + public string Alpha { get; set; } + } + + public class ColumnPaste + { + public string Title { get; set; } + public string ChooseColumn { get; set; } + public string OverwriteShiftCellsDown { get; set; } + public string Overwrite { get; set; } + public string ShiftCellsDown { get; set; } + public string TimeCodesOnly { get; set; } + public string TextOnly { get; set; } + public string OriginalTextOnly { get; set; } + } + + public class CompareSubtitles + { + public string Title { get; set; } + public string PreviousDifference { get; set; } + public string NextDifference { get; set; } + public string SubtitlesNotAlike { get; set; } + public string XNumberOfDifference { get; set; } + public string XNumberOfDifferenceAndPercentChanged { get; set; } + public string XNumberOfDifferenceAndPercentLettersChanged { get; set; } + public string ShowOnlyDifferences { get; set; } + public string IgnoreLineBreaks { get; set; } + public string OnlyLookForDifferencesInText { get; set; } + public string CannotCompareWithImageBasedSubtitles { get; set; } + } + + public class DCinemaProperties + { + public string Title { get; set; } + public string TitleSmpte { get; set; } + public string SubtitleId { get; set; } + public string GenerateId { get; set; } + public string MovieTitle { get; set; } + public string ReelNumber { get; set; } + public string Language { get; set; } + public string IssueDate { get; set; } + public string EditRate { get; set; } + public string TimeCodeRate { get; set; } + public string StartTime { get; set; } + public string Font { get; set; } + public string FontId { get; set; } + public string FontUri { get; set; } + public string FontColor { get; set; } + public string FontEffect { get; set; } + public string FontEffectColor { get; set; } + public string FontSize { get; set; } + public string TopBottomMargin { get; set; } + public string FadeUpTime { get; set; } + public string FadeDownTime { get; set; } + public string ZPosition { get; set; } + public string ZPositionHelp { get; set; } + public string ChooseColor { get; set; } + public string Generate { get; set; } + } + + public class DurationsBridgeGaps + { + public string Title { get; set; } + public string GapsBridgedX { get; set; } + public string GapToNext { get; set; } + public string BridgeGapsSmallerThanXPart1 { get; set; } + public string BridgeGapsSmallerThanXPart2 { get; set; } + public string MinMillisecondsBetweenLines { get; set; } + public string ProlongEndTime { get; set; } + public string DivideEven { get; set; } + } + + public class DvdSubRip + { + public string Title { get; set; } + public string DvdGroupTitle { get; set; } + public string IfoFile { get; set; } + public string IfoFiles { get; set; } + public string VobFiles { get; set; } + public string Add { get; set; } + public string Remove { get; set; } + public string Clear { get; set; } + public string MoveUp { get; set; } + public string MoveDown { get; set; } + public string Languages { get; set; } + public string PalNtsc { get; set; } + public string Pal { get; set; } + public string Ntsc { get; set; } + public string StartRipping { get; set; } + public string Abort { get; set; } + public string AbortedByUser { get; set; } + public string ReadingSubtitleData { get; set; } + public string RippingVobFileXofYZ { get; set; } + public string WrongIfoType { get; set; } + } + + public class DvdSubRipChooseLanguage + { + public string Title { get; set; } + public string ChooseLanguageStreamId { get; set; } + public string UnknownLanguage { get; set; } + public string SubtitleImageXofYAndWidthXHeight { get; set; } + public string SubtitleImage { get; set; } + } + + public class EbuSaveOptions + { + public string Title { get; set; } + public string GeneralSubtitleInformation { get; set; } + public string CodePageNumber { get; set; } + public string DiskFormatCode { get; set; } + public string DisplayStandardCode { get; set; } + public string CharacterCodeTable { get; set; } + public string LanguageCode { get; set; } + public string OriginalProgramTitle { get; set; } + public string OriginalEpisodeTitle { get; set; } + public string TranslatedProgramTitle { get; set; } + public string TranslatedEpisodeTitle { get; set; } + public string TranslatorsName { get; set; } + public string SubtitleListReferenceCode { get; set; } + public string CountryOfOrigin { get; set; } + public string TimeCodeStatus { get; set; } + public string TimeCodeStartOfProgramme { get; set; } + public string RevisionNumber { get; set; } + public string MaxNoOfDisplayableChars { get; set; } + public string MaxNumberOfDisplayableRows { get; set; } + public string DiskSequenceNumber { get; set; } + public string TotalNumberOfDisks { get; set; } + public string Import { get; set; } + public string TextAndTimingInformation { get; set; } + public string JustificationCode { get; set; } + public string Errors { get; set; } + public string ErrorsX { get; set; } + public string MaxLengthError { get; set; } + public string TextUnchangedPresentation { get; set; } + public string TextLeftJustifiedText { get; set; } + public string TextCenteredText { get; set; } + public string TextRightJustifiedText { get; set; } + } + + public class EffectKaraoke + { + public string Title { get; set; } + public string ChooseColor { get; set; } + public string TotalMilliseconds { get; set; } + public string EndDelayInMilliseconds { get; set; } + } + + public class EffectTypewriter + { + public string Title { get; set; } + public string TotalMilliseconds { get; set; } + public string EndDelayInMilliseconds { get; set; } + } + + public class ExportCustomText + { + public string Title { get; set; } + public string Formats { get; set; } + public string New { get; set; } + public string Edit { get; set; } + public string Delete { get; set; } + public string SaveAs { get; set; } + public string SaveSubtitleAs { get; set; } + public string SubtitleExportedInCustomFormatToX { get; set; } + } + + public class ExportCustomTextFormat + { + public string Title { get; set; } + public string Template { get; set; } + public string Header { get; set; } + public string TextLine { get; set; } + public string TimeCode { get; set; } + public string NewLine { get; set; } + public string Footer { get; set; } + public string DoNotModify { get; set; } + } + + public class ExportPngXml + { + public string Title { get; set; } + public string ImageSettings { get; set; } + public string FontFamily { get; set; } + public string FontSize { get; set; } + public string FontColor { get; set; } + public string BorderColor { get; set; } + public string BorderWidth { get; set; } + public string BorderStyle { get; set; } + public string BorderStyleOneBox { get; set; } + public string BorderStyleBoxForEachLine { get; set; } + public string BorderStyleNormalWidthX { get; set; } + public string ShadowColor { get; set; } + public string ShadowWidth { get; set; } + public string Transparency { get; set; } + public string ImageFormat { get; set; } + public string FullFrameImage { get; set; } + public string SimpleRendering { get; set; } + public string AntiAliasingWithTransparency { get; set; } + public string Text3D { get; set; } + public string SideBySide3D { get; set; } + public string HalfTopBottom3D { get; set; } + public string Depth { get; set; } + public string ExportAllLines { get; set; } + public string XImagesSavedInY { get; set; } + public string VideoResolution { get; set; } + public string Align { get; set; } + public string Left { get; set; } + public string Right { get; set; } + public string Center { get; set; } + public string BottomMargin { get; set; } + public string LeftRightMargin { get; set; } + public string SaveBluRraySupAs { get; set; } + public string SaveVobSubAs { get; set; } + public string SaveFabImageScriptAs { get; set; } + public string SaveDvdStudioProStlAs { get; set; } + public string SaveDigitalCinemaInteropAs { get; set; } + public string SavePremiereEdlAs { get; set; } + public string SaveFcpAs { get; set; } + public string SaveDostAs { get; set; } + public string SomeLinesWereTooLongX { get; set; } + public string LineHeight { get; set; } + public string BoxSingleLine { get; set; } + public string BoxMultiLine { get; set; } + public string Forced { get; set; } + public string ChooseBackgroundColor { get; set; } + public string SaveImageAs { get; set; } + } + + public class ExportText + { + public string Title { get; set; } + public string Preview { get; set; } + public string ExportOptions { get; set; } + public string FormatText { get; set; } + public string None { get; set; } + public string MergeAllLines { get; set; } + public string UnbreakLines { get; set; } + public string RemoveStyling { get; set; } + public string ShowLineNumbers { get; set; } + public string AddNewLineAfterLineNumber { get; set; } + public string ShowTimeCode { get; set; } + public string AddNewLineAfterTimeCode { get; set; } + public string AddNewLineAfterTexts { get; set; } + public string AddNewLineBetweenSubtitles { get; set; } + public string TimeCodeFormat { get; set; } + public string Srt { get; set; } + public string Milliseconds { get; set; } + public string HHMMSSFF { get; set; } + public string TimeCodeSeparator { get; set; } + } + + public class ExtractDateTimeInfo + { + public string Title { get; set; } + public string OpenVideoFile { get; set; } + public string StartFrom { get; set; } + public string DateTimeFormat { get; set; } + public string Example { get; set; } + public string GenerateSubtitle { get; set; } + } + + public class FindDialog + { + public string Title { get; set; } + public string Find { get; set; } + public string Normal { get; set; } + public string CaseSensitive { get; set; } + public string RegularExpression { get; set; } + } + + public class FindSubtitleLine + { + public string Title { get; set; } + public string Find { get; set; } + public string FindNext { get; set; } + } + + public class FixCommonErrors + { + public string Title { get; set; } + public string Step1 { get; set; } + public string WhatToFix { get; set; } + public string Example { get; set; } + public string SelectAll { get; set; } + public string InverseSelection { get; set; } + public string Back { get; set; } + public string Next { get; set; } + public string Step2 { get; set; } + public string Fixes { get; set; } + public string Log { get; set; } + public string Function { get; set; } + public string RemovedEmptyLine { get; set; } + public string RemovedEmptyLineAtTop { get; set; } + public string RemovedEmptyLineAtBottom { get; set; } + public string RemovedEmptyLinesUnsedLineBreaks { get; set; } + public string EmptyLinesRemovedX { get; set; } + public string FixOverlappingDisplayTimes { get; set; } + public string FixShortDisplayTimes { get; set; } + public string FixLongDisplayTimes { get; set; } + public string FixInvalidItalicTags { get; set; } + public string RemoveUnneededSpaces { get; set; } + public string RemoveUnneededPeriods { get; set; } + public string FixMissingSpaces { get; set; } + public string BreakLongLines { get; set; } + public string RemoveLineBreaks { get; set; } + public string RemoveLineBreaksAll { get; set; } + public string FixUppercaseIInsindeLowercaseWords { get; set; } + public string FixDoubleApostrophes { get; set; } + public string AddPeriods { get; set; } + public string StartWithUppercaseLetterAfterParagraph { get; set; } + public string StartWithUppercaseLetterAfterPeriodInsideParagraph { get; set; } + public string StartWithUppercaseLetterAfterColon { get; set; } + public string FixLowercaseIToUppercaseI { get; set; } + public string FixCommonOcrErrors { get; set; } + public string CommonOcrErrorsFixed { get; set; } + public string RemoveSpaceBetweenNumber { get; set; } + public string FixDialogsOnOneLine { get; set; } + public string RemoveSpaceBetweenNumbersFixed { get; set; } + public string FixTurkishAnsi { get; set; } + public string FixDanishLetterI { get; set; } + public string FixSpanishInvertedQuestionAndExclamationMarks { get; set; } + public string AddMissingQuote { get; set; } + public string AddMissingQuotes { get; set; } + public string FixHyphens { get; set; } + public string FixHyphensAdd { get; set; } + public string FixHyphen { get; set; } + public string XHyphensFixed { get; set; } + public string AddMissingQuotesExample { get; set; } + public string XMissingQuotesAdded { get; set; } + public string Fix3PlusLines { get; set; } + public string Fix3PlusLine { get; set; } + public string X3PlusLinesFixed { get; set; } + public string Analysing { get; set; } + public string NothingToFix { get; set; } + public string FixesFoundX { get; set; } + public string XFixesApplied { get; set; } + public string NothingToFixBut { get; set; } + public string FixLowercaseIToUppercaseICheckedButCurrentLanguageIsNotEnglish { get; set; } + public string Continue { get; set; } + public string ContinueAnyway { get; set; } + public string UncheckedFixLowercaseIToUppercaseI { get; set; } + public string XIsChangedToUppercase { get; set; } + public string FixFirstLetterToUppercaseAfterParagraph { get; set; } + public string MergeShortLine { get; set; } + public string MergeShortLineAll { get; set; } + public string XLineBreaksAdded { get; set; } + public string BreakLongLine { get; set; } + public string FixLongDisplayTime { get; set; } + public string FixInvalidItalicTag { get; set; } + public string FixShortDisplayTime { get; set; } + public string FixOverlappingDisplayTime { get; set; } + public string FixInvalidItalicTagsExample { get; set; } + public string RemoveUnneededSpacesExample { get; set; } + public string RemoveUnneededPeriodsExample { get; set; } + public string FixMissingSpacesExample { get; set; } + public string FixUppercaseIInsindeLowercaseWordsExample { get; set; } + public string FixLowercaseIToUppercaseIExample { get; set; } + public string StartTimeLaterThanEndTime { get; set; } + public string UnableToFixStartTimeLaterThanEndTime { get; set; } + public string XFixedToYZ { get; set; } + public string UnableToFixTextXY { get; set; } + public string XOverlappingTimestampsFixed { get; set; } + public string XDisplayTimesProlonged { get; set; } + public string XInvalidHtmlTagsFixed { get; set; } + public string XDisplayTimesShortned { get; set; } + public string XLinesUnbreaked { get; set; } + public string UnneededSpace { get; set; } + public string XUnneededSpacesRemoved { get; set; } + public string UnneededPeriod { get; set; } + public string XUnneededPeriodsRemoved { get; set; } + public string FixMissingSpace { get; set; } + public string XMissingSpacesAdded { get; set; } + public string FixUppercaseIInsideLowercaseWord { get; set; } + public string XPeriodsAdded { get; set; } + public string FixMissingPeriodAtEndOfLine { get; set; } + public string XDoubleApostrophesFixed { get; set; } + public string XUppercaseIsFoundInsideLowercaseWords { get; set; } + public string RefreshFixes { get; set; } + public string ApplyFixes { get; set; } + public string AutoBreak { get; set; } + public string Unbreak { get; set; } + public string FixDoubleDash { get; set; } + public string FixDoubleGreaterThan { get; set; } + public string FixEllipsesStart { get; set; } + public string FixMissingOpenBracket { get; set; } + public string FixMusicNotation { get; set; } + public string XFixDoubleDash { get; set; } + public string XFixDoubleGreaterThan { get; set; } + public string XFixEllipsesStart { get; set; } + public string XFixMissingOpenBracket { get; set; } + public string XFixMusicNotation { get; set; } + public string FixDoubleDashExample { get; set; } + public string FixDoubleGreaterThanExample { get; set; } + public string FixEllipsesStartExample { get; set; } + public string FixMissingOpenBracketExample { get; set; } + public string FixMusicNotationExample { get; set; } + public string NumberOfImportantLogMessages { get; set; } + public string FixedOkXY { get; set; } + public string FixOcrErrorExample { get; set; } + public string FixSpaceBetweenNumbersExample { get; set; } + public string FixDialogsOneLineExample { get; set; } + } + + public class GetDictionaries + { + public string Title { get; set; } + public string DescriptionLine1 { get; set; } + public string DescriptionLine2 { get; set; } + public string GetDictionariesHere { get; set; } + public string OpenOpenOfficeWiki { get; set; } + public string GetAllDictionaries { get; set; } + public string ChooseLanguageAndClickDownload { get; set; } + public string OpenDictionariesFolder { get; set; } + public string Download { get; set; } + public string XDownloaded { get; set; } + } + + public class GetTesseractDictionaries + { + public string Title { get; set; } + public string DescriptionLine1 { get; set; } + public string DownloadFailed { get; set; } + public string GetDictionariesHere { get; set; } + public string OpenOpenOfficeWiki { get; set; } + public string GetAllDictionaries { get; set; } + public string ChooseLanguageAndClickDownload { get; set; } + public string OpenDictionariesFolder { get; set; } + public string Download { get; set; } + public string XDownloaded { get; set; } + } + + public class GoogleTranslate + { + public string Title { get; set; } + public string From { get; set; } + public string To { get; set; } + public string Translate { get; set; } + public string PleaseWait { get; set; } + public string PoweredByGoogleTranslate { get; set; } + public string PoweredByMicrosoftTranslate { get; set; } + } + + public class GoogleOrMicrosoftTranslate + { + public string Title { get; set; } + public string From { get; set; } + public string To { get; set; } + public string Translate { get; set; } + public string SourceText { get; set; } + public string GoogleTranslate { get; set; } + public string MicrosoftTranslate { get; set; } + } + + public class GoToLine + { + public string Title { get; set; } + public string XIsNotAValidNumber { get; set; } + } + + public class ImportImages + { + public string Title { get; set; } + public string ImageFiles { get; set; } + public string Input { get; set; } + public string InputDescription { get; set; } + } + + public class ImportSceneChanges + { + public string Title { get; set; } + public string OpenTextFile { get; set; } + public string ImportOptions { get; set; } + public string TextFiles { get; set; } + public string TimeCodes { get; set; } + public string Frames { get; set; } + public string Seconds { get; set; } + public string Milliseconds { get; set; } + } + + public class ImportText + { + public string Title { get; set; } + public string OneSubtitleIsOneFile { get; set; } + public string OpenTextFile { get; set; } + public string OpenTextFiles { get; set; } + public string ImportOptions { get; set; } + public string Splitting { get; set; } + public string AutoSplitText { get; set; } + public string OneLineIsOneSubtitle { get; set; } + public string LineBreak { get; set; } + public string SplitAtBlankLines { get; set; } + public string MergeShortLines { get; set; } + public string RemoveEmptyLines { get; set; } + public string RemoveLinesWithoutLetters { get; set; } + public string GenerateTimeCodes { get; set; } + public string GapBetweenSubtitles { get; set; } + public string Auto { get; set; } + public string Fixed { get; set; } + public string Refresh { get; set; } + public string TextFiles { get; set; } + public string PreviewLinesModifiedX { get; set; } + public string TimeCodes { get; set; } + } + + public class Interjections + { + public string Title { get; set; } + } + + public class JoinSubtitles + { + public string Title { get; set; } + public string Information { get; set; } + public string NumberOfLines { get; set; } + public string StartTime { get; set; } + public string EndTime { get; set; } + public string FileName { get; set; } + public string Join { get; set; } + public string TotalNumberOfLinesX { get; set; } + public string Note { get; set; } + } + + public class Main + { + public MainMenu Menu { get; set; } + public MainControls Controls { get; set; } + public MainVideoControls VideoControls { get; set; } + public string SaveChangesToUntitled { get; set; } + public string SaveChangesToX { get; set; } + public string SaveChangesToUntitledOriginal { get; set; } + public string SaveChangesToOriginalX { get; set; } + public string SaveSubtitleAs { get; set; } + public string SaveOriginalSubtitleAs { get; set; } + public string NoSubtitleLoaded { get; set; } + public string VisualSyncSelectedLines { get; set; } + public string VisualSyncTitle { get; set; } + public string BeforeVisualSync { get; set; } + public string VisualSyncPerformedOnSelectedLines { get; set; } + public string VisualSyncPerformed { get; set; } + public string ImportThisVobSubSubtitle { get; set; } + public string FileXIsLargerThan10MB { get; set; } + public string ContinueAnyway { get; set; } + public string BeforeLoadOf { get; set; } + public string LoadedSubtitleX { get; set; } + public string LoadedEmptyOrShort { get; set; } + public string FileIsEmptyOrShort { get; set; } + public string FileNotFound { get; set; } + public string SavedSubtitleX { get; set; } + public string SavedOriginalSubtitleX { get; set; } + public string FileOnDiskModified { get; set; } + public string OverwriteModifiedFile { get; set; } + public string FileXIsReadOnly { get; set; } + public string UnableToSaveSubtitleX { get; set; } + public string BeforeNew { get; set; } + public string New { get; set; } + public string BeforeConvertingToX { get; set; } + public string ConvertedToX { get; set; } + public string BeforeShowEarlier { get; set; } + public string BeforeShowLater { get; set; } + public string LineNumberX { get; set; } + public string OpenVideoFile { get; set; } + public string NewFrameRateUsedToCalculateTimeCodes { get; set; } + public string NewFrameRateUsedToCalculateFrameNumbers { get; set; } + public string FindContinue { get; set; } + public string FindContinueTitle { get; set; } + public string ReplaceContinueNotFound { get; set; } + public string ReplaceXContinue { get; set; } + public string ReplaceContinueTitle { get; set; } + public string SearchingForXFromLineY { get; set; } + public string XFoundAtLineNumberY { get; set; } + public string XNotFound { get; set; } + public string BeforeReplace { get; set; } + public string MatchFoundX { get; set; } + public string NoMatchFoundX { get; set; } + public string FoundNothingToReplace { get; set; } + public string ReplaceCountX { get; set; } + public string NoXFoundAtLineY { get; set; } + public string OneReplacementMade { get; set; } + public string BeforeChangesMadeInSourceView { get; set; } + public string UnableToParseSourceView { get; set; } + public string GoToLineNumberX { get; set; } + public string CreateAdjustChangesApplied { get; set; } + public string SelectedLines { get; set; } + public string BeforeDisplayTimeAdjustment { get; set; } + public string DisplayTimeAdjustedX { get; set; } + public string DisplayTimesAdjustedX { get; set; } + public string StarTimeAdjustedX { get; set; } + public string BeforeCommonErrorFixes { get; set; } + public string CommonErrorsFixedInSelectedLines { get; set; } + public string CommonErrorsFixed { get; set; } + public string BeforeRenumbering { get; set; } + public string RenumberedStartingFromX { get; set; } + public string BeforeRemovalOfTextingForHearingImpaired { get; set; } + public string TextingForHearingImpairedRemovedOneLine { get; set; } + public string TextingForHearingImpairedRemovedXLines { get; set; } + public string SubtitleSplitted { get; set; } + public string SubtitleAppendPrompt { get; set; } + public string SubtitleAppendPromptTitle { get; set; } + public string OpenSubtitleToAppend { get; set; } + public string AppendViaVisualSyncTitle { get; set; } + public string AppendSynchronizedSubtitlePrompt { get; set; } + public string BeforeAppend { get; set; } + public string SubtitleAppendedX { get; set; } + public string SubtitleNotAppended { get; set; } + public string GoogleTranslate { get; set; } + public string MicrosoftTranslate { get; set; } + public string BeforeGoogleTranslation { get; set; } + public string SelectedLinesTranslated { get; set; } + public string SubtitleTranslated { get; set; } + public string TranslateSwedishToDanish { get; set; } + public string TranslateSwedishToDanishWarning { get; set; } + public string TranslatingViaNikseDkMt { get; set; } + public string BeforeSwedishToDanishTranslation { get; set; } + public string TranslationFromSwedishToDanishComplete { get; set; } + public string TranslationFromSwedishToDanishFailed { get; set; } + public string BeforeUndo { get; set; } + public string UndoPerformed { get; set; } + public string RedoPerformed { get; set; } + public string NothingToUndo { get; set; } + public string InvalidLanguageNameX { get; set; } + public string UnableToChangeLanguage { get; set; } + public string DoNotDisplayMessageAgain { get; set; } + public string NumberOfCorrectedWords { get; set; } + public string NumberOfSkippedWords { get; set; } + public string NumberOfCorrectWords { get; set; } + public string NumberOfWordsAddedToDictionary { get; set; } + public string NumberOfNameHits { get; set; } + public string SpellCheck { get; set; } + public string BeforeSpellCheck { get; set; } + public string SpellCheckChangedXToY { get; set; } + public string BeforeAddingTagX { get; set; } + public string TagXAdded { get; set; } + public string LineXOfY { get; set; } + public string XLinesSavedAsY { get; set; } + public string XLinesDeleted { get; set; } + public string BeforeDeletingXLines { get; set; } + public string DeleteXLinesPrompt { get; set; } + public string OneLineDeleted { get; set; } + public string BeforeDeletingOneLine { get; set; } + public string DeleteOneLinePrompt { get; set; } + public string BeforeInsertLine { get; set; } + public string LineInserted { get; set; } + public string BeforeLineUpdatedInListView { get; set; } + public string BeforeSettingFontToNormal { get; set; } + public string BeforeSplitLine { get; set; } + public string LineSplitted { get; set; } + public string BeforeMergeLines { get; set; } + public string LinesMerged { get; set; } + public string BeforeSettingColor { get; set; } + public string BeforeSettingFontName { get; set; } + public string BeforeTypeWriterEffect { get; set; } + public string BeforeKaraokeEffect { get; set; } + public string BeforeImportingDvdSubtitle { get; set; } + public string OpenMatroskaFile { get; set; } + public string MatroskaFiles { get; set; } + public string NoSubtitlesFound { get; set; } + public string NotAValidMatroskaFileX { get; set; } + public string BlurayNotSubtitlesFound { get; set; } + public string ParsingMatroskaFile { get; set; } + public string ParsingTransportStreamFile { get; set; } + public string BeforeImportFromMatroskaFile { get; set; } + public string SubtitleImportedFromMatroskaFile { get; set; } + public string DropFileXNotAccepted { get; set; } + public string DropOnlyOneFile { get; set; } + public string BeforeCreateAdjustLines { get; set; } + public string OpenAnsiSubtitle { get; set; } + public string BeforeChangeCasing { get; set; } + public string CasingCompleteMessageNoNames { get; set; } + public string CasingCompleteMessageOnlyNames { get; set; } + public string CasingCompleteMessage { get; set; } + public string BeforeChangeFrameRate { get; set; } + public string BeforeAdjustSpeedInPercent { get; set; } + public string FrameRateChangedFromXToY { get; set; } + public string IdxFileNotFoundWarning { get; set; } + public string InvalidVobSubHeader { get; set; } + public string OpenVobSubFile { get; set; } + public string VobSubFiles { get; set; } + public string OpenBluRaySupFile { get; set; } + public string BluRaySupFiles { get; set; } + public string OpenXSubFiles { get; set; } + public string XSubFiles { get; set; } + public string BeforeImportingVobSubFile { get; set; } + public string BeforeImportingBluRaySupFile { get; set; } + public string BeforeImportingBdnXml { get; set; } + public string BeforeShowSelectedLinesEarlierLater { get; set; } + public string ShowAllLinesXSecondsLinesEarlier { get; set; } + public string ShowAllLinesXSecondsLinesLater { get; set; } + public string ShowSelectedLinesXSecondsLinesEarlier { get; set; } + public string ShowSelectedLinesXSecondsLinesLater { get; set; } + public string ShowSelectionAndForwardXSecondsLinesEarlier { get; set; } + public string ShowSelectionAndForwardXSecondsLinesLater { get; set; } + public string ShowSelectedLinesEarlierLaterPerformed { get; set; } + public string DoubleWordsViaRegEx { get; set; } + public string BeforeSortX { get; set; } + public string SortedByX { get; set; } + public string BeforeAutoBalanceSelectedLines { get; set; } + public string NumberOfLinesAutoBalancedX { get; set; } + public string BeforeRemoveLineBreaksInSelectedLines { get; set; } + public string NumberOfWithRemovedLineBreakX { get; set; } + public string BeforeMultipleReplace { get; set; } + public string NumberOfLinesReplacedX { get; set; } + public string NameXAddedToNamesEtcList { get; set; } + public string NameXNotAddedToNamesEtcList { get; set; } + public string WordXAddedToUserDic { get; set; } + public string WordXNotAddedToUserDic { get; set; } + public string OcrReplacePairXAdded { get; set; } + public string OcrReplacePairXNotAdded { get; set; } + public string XLinesSelected { get; set; } + public string UnicodeMusicSymbolsAnsiWarning { get; set; } + public string UnicodeCharactersAnsiWarning { get; set; } + public string NegativeTimeWarning { get; set; } + public string BeforeMergeShortLines { get; set; } + public string BeforeSplitLongLines { get; set; } + public string MergedShortLinesX { get; set; } + public string BeforeDurationsBridgeGap { get; set; } + public string BeforeSetMinimumDisplayTimeBetweenParagraphs { get; set; } + public string XMinimumDisplayTimeBetweenParagraphsChanged { get; set; } + public string BeforeImportText { get; set; } + public string TextImported { get; set; } + public string BeforePointSynchronization { get; set; } + public string PointSynchronizationDone { get; set; } + public string BeforeTimeCodeImport { get; set; } + public string TimeCodeImportedFromXY { get; set; } + public string BeforeInsertSubtitleAtVideoPosition { get; set; } + public string BeforeSetStartTimeAndOffsetTheRest { get; set; } + public string BeforeSetEndTimeAndOffsetTheRest { get; set; } + public string BeforeSetEndAndVideoPosition { get; set; } + public string ContinueWithCurrentSpellCheck { get; set; } + public string CharactersPerSecond { get; set; } + public string GetFrameRateFromVideoFile { get; set; } + public string NetworkMessage { get; set; } + public string NetworkUpdate { get; set; } + public string NetworkInsert { get; set; } + public string NetworkDelete { get; set; } + public string NetworkNewUser { get; set; } + public string NetworkByeUser { get; set; } + public string NetworkUnableToConnectToServer { get; set; } + public string UserAndAction { get; set; } + public string NetworkMode { get; set; } + public string XStartedSessionYAtZ { get; set; } + public string SpellChekingViaWordXLineYOfX { get; set; } + public string UnableToStartWord { get; set; } + public string SpellCheckAbortedXCorrections { get; set; } + public string SpellCheckCompletedXCorrections { get; set; } + public string OpenOtherSubtitle { get; set; } + public string BeforeToggleDialogDashes { get; set; } + public string ExportPlainTextAs { get; set; } + public string TextFiles { get; set; } + public string SubtitleExported { get; set; } + public string LineNumberXErrorReadingFromSourceLineY { get; set; } + public string LineNumberXErrorReadingTimeCodeFromSourceLineY { get; set; } + public string LineNumberXExpectedNumberFromSourceLineY { get; set; } + public string BeforeGuessingTimeCodes { get; set; } + public string BeforeAutoDuration { get; set; } + public string BeforeColumnPaste { get; set; } + public string BeforeColumnDelete { get; set; } + public string BeforeColumnImportText { get; set; } + public string BeforeColumnShiftCellsDown { get; set; } + public string ErrorLoadingPluginXErrorY { get; set; } + public string BeforeRunningPluginXVersionY { get; set; } + public string UnableToReadPluginResult { get; set; } + public string UnableToCreateBackupDirectory { get; set; } + public string BeforeDisplaySubtitleJoin { get; set; } + public string SubtitlesJoined { get; set; } + public string StatusLog { get; set; } + public string XSceneChangesImported { get; set; } + public string PluginXExecuted { get; set; } + public string NotAValidXSubFile { get; set; } + public string BeforeMergeLinesWithSameText { get; set; } + public string ImportTimeCodesDifferentNumberOfLinesWarning { get; set; } + public string ParsingTransportStream { get; set; } + public string XPercentCompleted { get; set; } + public string ErrorLoadIdx { get; set; } + public string ErrorLoadRar { get; set; } + public string ErrorLoadZip { get; set; } + public string ErrorLoadPng { get; set; } + public string ErrorLoadJpg { get; set; } + public string ErrorLoadSrr { get; set; } + public string ErrorLoadTorrent { get; set; } + public string ErrorLoadBinaryZeroes { get; set; } + public string ErrorDirectoryDropNotAllowed { get; set; } + public string NoSupportEncryptedVobSub { get; set; } + public string NoSupportHereBluRaySup { get; set; } + public string NoSupportHereDvdSup { get; set; } + public string NoSupportHereVobSub { get; set; } + public string NoSupportHereDivx { get; set; } + + public class MainMenu + { + public class FileMenu + { + public string Title { get; set; } + public string New { get; set; } + public string Open { get; set; } + public string OpenKeepVideo { get; set; } + public string Reopen { get; set; } + public string Save { get; set; } + public string SaveAs { get; set; } + public string RestoreAutoBackup { get; set; } + public string AdvancedSubStationAlphaProperties { get; set; } + public string SubStationAlphaProperties { get; set; } + public string EbuProperties { get; set; } + public string PacProperties { get; set; } + public string OpenOriginal { get; set; } + public string SaveOriginal { get; set; } + public string CloseOriginal { get; set; } + public string OpenContainingFolder { get; set; } + public string Compare { get; set; } + public string Statistics { get; set; } + public string Plugins { get; set; } + public string ImportOcrFromDvd { get; set; } + public string ImportOcrVobSubSubtitle { get; set; } + public string ImportBluRaySupFile { get; set; } + public string ImportXSub { get; set; } + public string ImportSubtitleFromMatroskaFile { get; set; } + public string ImportSubtitleWithManualChosenEncoding { get; set; } + public string ImportText { get; set; } + public string ImportImages { get; set; } + public string ImportTimecodes { get; set; } + public string Export { get; set; } + public string ExportBdnXml { get; set; } + public string ExportBluRaySup { get; set; } + public string ExportVobSub { get; set; } + public string ExportCavena890 { get; set; } + public string ExportEbu { get; set; } + public string ExportPac { get; set; } + public string ExportPlainText { get; set; } + public string ExportAdobeEncoreFabImageScript { get; set; } + public string ExportKoreanAtsFilePair { get; set; } + public string ExportAvidStl { get; set; } + public string ExportDvdStudioProStl { get; set; } + public string ExportCapMakerPlus { get; set; } + public string ExportCaptionsInc { get; set; } + public string ExportCheetahCap { get; set; } + public string ExportUltech130 { get; set; } + public string ExportCustomTextFormat { get; set; } + public string Exit { get; set; } + } + public class EditMenu + { + public string Title { get; set; } + public string Undo { get; set; } + public string Redo { get; set; } + public string ShowUndoHistory { get; set; } + public string InsertUnicodeSymbol { get; set; } + public string InsertUnicodeControlCharacters { get; set; } + public string InsertUnicodeControlCharactersLRM { get; set; } + public string InsertUnicodeControlCharactersRLM { get; set; } + public string InsertUnicodeControlCharactersLRE { get; set; } + public string InsertUnicodeControlCharactersRLE { get; set; } + public string InsertUnicodeControlCharactersLRO { get; set; } + public string InsertUnicodeControlCharactersRLO { get; set; } + public string Find { get; set; } + public string FindNext { get; set; } + public string Replace { get; set; } + public string MultipleReplace { get; set; } + public string GoToSubtitleNumber { get; set; } + public string RightToLeftMode { get; set; } + public string FixTrlViaUnicodeControlCharacters { get; set; } + public string ReverseRightToLeftStartEnd { get; set; } + public string ShowOriginalTextInAudioAndVideoPreview { get; set; } + public string ModifySelection { get; set; } + public string InverseSelection { get; set; } + } + public class ToolsMenu + { + public string Title { get; set; } + public string AdjustDisplayDuration { get; set; } + public string ApplyDurationLimits { get; set; } + public string DurationsBridgeGap { get; set; } + public string FixCommonErrors { get; set; } + public string StartNumberingFrom { get; set; } + public string RemoveTextForHearingImpaired { get; set; } + public string ChangeCasing { get; set; } + public string ChangeFrameRate { get; set; } + public string ChangeSpeedInPercent { get; set; } + public string MergeShortLines { get; set; } + public string MergeDuplicateText { get; set; } + public string MergeSameTimeCodes { get; set; } + public string SplitLongLines { get; set; } + public string MinimumDisplayTimeBetweenParagraphs { get; set; } + public string SortBy { get; set; } + public string Number { get; set; } + public string StartTime { get; set; } + public string EndTime { get; set; } + public string Duration { get; set; } + public string TextAlphabetically { get; set; } + public string TextSingleLineMaximumLength { get; set; } + public string TextTotalLength { get; set; } + public string TextNumberOfLines { get; set; } + public string TextNumberOfCharactersPerSeconds { get; set; } + public string WordsPerMinute { get; set; } + public string Style { get; set; } + public string Ascending { get; set; } + public string Descending { get; set; } + public string MakeNewEmptyTranslationFromCurrentSubtitle { get; set; } + public string BatchConvert { get; set; } + public string GenerateTimeAsText { get; set; } + public string MeasurementConverter { get; set; } + public string SplitSubtitle { get; set; } + public string AppendSubtitle { get; set; } + public string JoinSubtitles { get; set; } + } + public class VideoMenu + { + public string Title { get; set; } + public string OpenVideo { get; set; } + public string OpenDvd { get; set; } + public string ChooseAudioTrack { get; set; } + public string CloseVideo { get; set; } + public string ImportSceneChanges { get; set; } + public string RemoveSceneChanges { get; set; } + public string WaveformBatchGenerate { get; set; } + public string ShowHideVideo { get; set; } + public string ShowHideWaveform { get; set; } + public string ShowHideWaveformAndSpectrogram { get; set; } + public string UnDockVideoControls { get; set; } + public string ReDockVideoControls { get; set; } + } + public class SpellCheckMenu + { + public string Title { get; set; } + public string SpellCheck { get; set; } + public string SpellCheckFromCurrentLine { get; set; } + public string FindDoubleWords { get; set; } + public string FindDoubleLines { get; set; } + public string GetDictionaries { get; set; } + public string AddToNamesEtcList { get; set; } + } + public class SynchronizationkMenu + { + public string Title { get; set; } + public string AdjustAllTimes { get; set; } + public string VisualSync { get; set; } + public string PointSync { get; set; } + public string PointSyncViaOtherSubtitle { get; set; } + } + public class AutoTranslateMenu + { + public string Title { get; set; } + public string TranslatePoweredByGoogle { get; set; } + public string TranslatePoweredByMicrosoft { get; set; } + public string TranslateFromSwedishToDanish { get; set; } + } + public class OptionsMenu + { + public string Title { get; set; } + public string Settings { get; set; } + public string ChooseLanguage { get; set; } + } + + public class NetworkingMenu + { + public string Title { get; set; } + public string StartNewSession { get; set; } + public string JoinSession { get; set; } + public string ShowSessionInfoAndLog { get; set; } + public string Chat { get; set; } + public string LeaveSession { get; set; } + } + + public class HelpMenu + { + public string CheckForUpdates { get; set; } + public string Title { get; set; } + public string Help { get; set; } + public string About { get; set; } + } + + public class ToolBarMenu + { + public string New { get; set; } + public string Open { get; set; } + public string Save { get; set; } + public string SaveAs { get; set; } + public string Find { get; set; } + public string Replace { get; set; } + public string FixCommonErrors { get; set; } + public string VisualSync { get; set; } + public string SpellCheck { get; set; } + public string Settings { get; set; } + public string Help { get; set; } + public string ShowHideWaveform { get; set; } + public string ShowHideVideo { get; set; } + } + + public class ListViewContextMenu + { + public string AdvancedSubStationAlphaSetStyle { get; set; } + public string SubStationAlphaSetStyle { get; set; } + public string SubStationAlphaStyles { get; set; } + public string AdvancedSubStationAlphaStyles { get; set; } + public string TimedTextSetStyle { get; set; } + public string TimedTextStyles { get; set; } + public string TimedTextSetLanguage { get; set; } + public string SamiSetStyle { get; set; } + public string NuendoSetStyle { get; set; } + public string Cut { get; set; } + public string Copy { get; set; } + public string Paste { get; set; } + public string Delete { get; set; } + public string SplitLineAtCursorPosition { get; set; } + public string AutoDurationCurrentLine { get; set; } + public string SelectAll { get; set; } + public string InsertFirstLine { get; set; } + public string InsertBefore { get; set; } + public string InsertAfter { get; set; } + public string InsertSubtitleAfter { get; set; } + public string CopyToClipboard { get; set; } + public string Column { get; set; } + public string ColumnDeleteText { get; set; } + public string ColumnDeleteTextAndShiftCellsUp { get; set; } + public string ColumnInsertEmptyTextAndShiftCellsDown { get; set; } + public string ColumnInsertTextFromSubtitle { get; set; } + public string ColumnImportTextAndShiftCellsDown { get; set; } + public string ColumnPasteFromClipboard { get; set; } + public string ColumnCopyOriginalTextToCurrent { get; set; } + public string Split { get; set; } + public string MergeSelectedLines { get; set; } + public string MergeSelectedLinesAsDialog { get; set; } + public string MergeWithLineBefore { get; set; } + public string MergeWithLineAfter { get; set; } + public string Normal { get; set; } + public string Underline { get; set; } + public string Color { get; set; } + public string FontName { get; set; } + public string Alignment { get; set; } + public string AutoBalanceSelectedLines { get; set; } + public string RemoveLineBreaksFromSelectedLines { get; set; } + public string TypewriterEffect { get; set; } + public string KaraokeEffect { get; set; } + public string ShowSelectedLinesEarlierLater { get; set; } + public string VisualSyncSelectedLines { get; set; } + public string GoogleAndMicrosoftTranslateSelectedLine { get; set; } + public string GoogleTranslateSelectedLines { get; set; } + public string AdjustDisplayDurationForSelectedLines { get; set; } + public string FixCommonErrorsInSelectedLines { get; set; } + public string ChangeCasingForSelectedLines { get; set; } + public string SaveSelectedLines { get; set; } + public string WebVTTSetNewVoice { get; set; } + public string WebVTTRemoveVoices { get; set; } + } + + public FileMenu File { get; set; } + public EditMenu Edit { get; set; } + public ToolsMenu Tools { get; set; } + public VideoMenu Video { get; set; } + public SpellCheckMenu SpellCheck { get; set; } + public SynchronizationkMenu Synchronization { get; set; } + public AutoTranslateMenu AutoTranslate { get; set; } + public OptionsMenu Options { get; set; } + public NetworkingMenu Networking { get; set; } + public HelpMenu Help { get; set; } + public ToolBarMenu ToolBar { get; set; } + public ListViewContextMenu ContextMenu { get; set; } + } + + public class MainControls + { + public string SubtitleFormat { get; set; } + public string FileEncoding { get; set; } + public string ListView { get; set; } + public string SourceView { get; set; } + public string UndoChangesInEditPanel { get; set; } + public string Previous { get; set; } + public string Next { get; set; } + public string AutoBreak { get; set; } + public string Unbreak { get; set; } + } + + public class MainVideoControls + { + public string Translate { get; set; } + public string Create { get; set; } + public string Adjust { get; set; } + public string SelectCurrentElementWhilePlaying { get; set; } + + //translation helper + public string AutoRepeat { get; set; } + public string AutoRepeatOn { get; set; } + public string AutoRepeatCount { get; set; } + public string AutoContinue { get; set; } + public string AutoContinueOn { get; set; } + public string DelayInSeconds { get; set; } + public string OriginalText { get; set; } + public string Previous { get; set; } + public string Stop { get; set; } + public string PlayCurrent { get; set; } + public string Next { get; set; } + public string Playing { get; set; } + public string RepeatingLastTime { get; set; } + public string RepeatingXTimesLeft { get; set; } + public string AutoContinueInOneSecond { get; set; } + public string AutoContinueInXSeconds { get; set; } + public string StillTypingAutoContinueStopped { get; set; } + + // create/adjust + public string InsertNewSubtitleAtVideoPosition { get; set; } + public string Auto { get; set; } + public string PlayFromJustBeforeText { get; set; } + public string Pause { get; set; } + public string GoToSubtitlePositionAndPause { get; set; } + public string SetStartTime { get; set; } + public string SetEndTimeAndGoToNext { get; set; } + public string AdjustedViaEndTime { get; set; } + public string SetEndTime { get; set; } + public string SetstartTimeAndOffsetOfRest { get; set; } + + public string SearchTextOnline { get; set; } + public string GoogleTranslate { get; set; } + public string GoogleIt { get; set; } + public string SecondsBackShort { get; set; } + public string SecondsForwardShort { get; set; } + public string VideoPosition { get; set; } + public string TranslateTip { get; set; } + public string CreateTip { get; set; } + public string AdjustTip { get; set; } + + public string BeforeChangingTimeInWaveformX { get; set; } + public string NewTextInsertAtX { get; set; } + + public string Center { get; set; } + public string PlayRate { get; set; } + public string Slow { get; set; } + public string Normal { get; set; } + public string Fast { get; set; } + public string VeryFast { get; set; } + } + } + + public class MatroskaSubtitleChooser + { + public string Title { get; set; } + public string PleaseChoose { get; set; } + public string TrackXLanguageYTypeZ { get; set; } + } + + public class MeasurementConverter + { + public string Title { get; set; } + public string ConvertFrom { get; set; } + public string ConvertTo { get; set; } + public string CopyToClipboard { get; set; } + public string Celsius { get; set; } + public string Fahrenheit { get; set; } + public string Miles { get; set; } + public string Kilometers { get; set; } + public string Meters { get; set; } + public string Yards { get; set; } + public string Feet { get; set; } + public string Inches { get; set; } + public string Pounds { get; set; } + public string Kilos { get; set; } + } + + public class MergeDoubleLines + { + public string Title { get; set; } + public string MaxMillisecondsBetweenLines { get; set; } + public string IncludeIncrementing { get; set; } + } + + public class MergeShortLines + { + public string Title { get; set; } + public string MaximumCharacters { get; set; } + public string MaximumMillisecondsBetween { get; set; } + public string NumberOfMergesX { get; set; } + public string MergedText { get; set; } + public string OnlyMergeContinuationLines { get; set; } + } + + public class MergeTextWithSameTimeCodes + { + public string Title { get; set; } + public string MaxDifferenceMilliseconds { get; set; } + public string ReBreakLines { get; set; } + public string NumberOfMergesX { get; set; } + public string MergedText { get; set; } + } + + public class ModifySelection + { + public string Title { get; set; } + public string Rule { get; set; } + public string CaseSensitive { get; set; } + public string DoWithMatches { get; set; } + public string MakeNewSelection { get; set; } + public string AddToCurrentSelection { get; set; } + public string SubtractFromCurrentSelection { get; set; } + public string IntersectWithCurrentSelection { get; set; } + public string MatchingLinesX { get; set; } + public string Contains { get; set; } + public string StartsWith { get; set; } + public string EndsWith { get; set; } + public string NoContains { get; set; } + public string RegEx { get; set; } + public string UnequalLines { get; set; } + public string EqualLines { get; set; } + } + + public class MultipleReplace + { + public string Title { get; set; } + public string FindWhat { get; set; } + public string ReplaceWith { get; set; } + public string Normal { get; set; } + public string CaseSensitive { get; set; } + public string RegularExpression { get; set; } + public string LinesFoundX { get; set; } + public string Delete { get; set; } + public string Add { get; set; } + public string Update { get; set; } + public string Enabled { get; set; } + public string SearchType { get; set; } + public string RemoveAll { get; set; } + public string Import { get; set; } + public string Export { get; set; } + public string ImportRulesTitle { get; set; } + public string ExportRulesTitle { get; set; } + public string Rules { get; set; } + public string MoveToTop { get; set; } + public string MoveToBottom { get; set; } + } + + public class NetworkChat + { + public string Title { get; set; } + public string Send { get; set; } + } + + public class NetworkJoin + { + public string Title { get; set; } + public string Information { get; set; } + public string Join { get; set; } + } + + public class NetworkLogAndInfo + { + public string Title { get; set; } + public string Log { get; set; } + } + + public class NetworkStart + { + public string Title { get; set; } + public string ConnectionTo { get; set; } + public string Information { get; set; } + public string Start { get; set; } + } + + public class OpenVideoDvd + { + public string Title { get; set; } + public string OpenDvdFrom { get; set; } + public string Disc { get; set; } + public string Folder { get; set; } + public string ChooseDrive { get; set; } + public string ChooseFolder { get; set; } + } + + public class PluginsGet + { + public string Title { get; set; } + public string InstalledPlugins { get; set; } + public string GetPlugins { get; set; } + public string Description { get; set; } + public string Version { get; set; } + public string Date { get; set; } + public string Type { get; set; } + public string OpenPluginsFolder { get; set; } + public string GetPluginsInfo1 { get; set; } + public string GetPluginsInfo2 { get; set; } + public string PluginXDownloaded { get; set; } + public string Download { get; set; } + public string Remove { get; set; } + public string UpdateAllX { get; set; } + public string UnableToDownloadPluginListX { get; set; } + public string NewVersionOfSubtitleEditRequired { get; set; } + public string UpdateAvailable { get; set; } + public string UpdateAll { get; set; } + public string XPluginsUpdated { get; set; } + } + + public class RegularExpressionContextMenu + { + public string WordBoundary { get; set; } + public string NonWordBoundary { get; set; } + public string NewLine { get; set; } + public string NewLineShort { get; set; } + public string AnyDigit { get; set; } + public string NonDigit { get; set; } + public string AnyCharacter { get; set; } + public string AnyWhitespace { get; set; } + public string NonSpaceCharacter { get; set; } + public string ZeroOrMore { get; set; } + public string OneOrMore { get; set; } + public string InCharacterGroup { get; set; } + public string NotInCharacterGroup { get; set; } + } + + public class RemoveTextFromHearImpaired + { + public string Title { get; set; } + public string RemoveTextConditions { get; set; } + public string RemoveTextBetween { get; set; } + public string SquareBrackets { get; set; } + public string Brackets { get; set; } + public string Parentheses { get; set; } + public string QuestionMarks { get; set; } + public string And { get; set; } + public string RemoveTextBeforeColon { get; set; } + public string OnlyIfTextIsUppercase { get; set; } + public string OnlyIfInSeparateLine { get; set; } + public string LinesFoundX { get; set; } + public string RemoveTextIfContains { get; set; } + public string RemoveTextIfAllUppercase { get; set; } + public string RemoveInterjections { get; set; } + public string EditInterjections { get; set; } + } + + public class ReplaceDialog + { + public string Title { get; set; } + public string FindWhat { get; set; } + public string Normal { get; set; } + public string CaseSensitive { get; set; } + public string RegularExpression { get; set; } + public string ReplaceWith { get; set; } + public string Find { get; set; } + public string Replace { get; set; } + public string ReplaceAll { get; set; } + } + + public class RestoreAutoBackup + { + public string Title { get; set; } + public string Information { get; set; } + public string DateAndTime { get; set; } + public string FileName { get; set; } + public string Extension { get; set; } + public string NoBackedUpFilesFound { get; set; } + } + + public class SeekSilence + { + public string Title { get; set; } + public string SearchDirection { get; set; } + public string Forward { get; set; } + public string Back { get; set; } + public string LengthInSeconds { get; set; } + public string MaxVolume { get; set; } + } + + public class SetMinimumDisplayTimeBetweenParagraphs + { + public string Title { get; set; } + public string PreviewLinesModifiedX { get; set; } + public string ShowOnlyModifiedLines { get; set; } + public string MinimumMillisecondsBetweenParagraphs { get; set; } + public string FrameInfo { get; set; } + public string OneFrameXisYMilliseconds { get; set; } + } + + public class SetSyncPoint + { + public string Title { get; set; } + public string SyncPointTimeCode { get; set; } + public string ThreeSecondsBack { get; set; } + public string HalfASecondBack { get; set; } + public string HalfASecondForward { get; set; } + public string ThreeSecondsForward { get; set; } + } + + public class Settings + { + public string Title { get; set; } + public string General { get; set; } + public string Toolbar { get; set; } + public string VideoPlayer { get; set; } + public string WaveformAndSpectrogram { get; set; } + public string Tools { get; set; } + public string WordLists { get; set; } + public string SsaStyle { get; set; } + public string Network { get; set; } + public string ShowToolBarButtons { get; set; } + public string New { get; set; } + public string Open { get; set; } + public string Save { get; set; } + public string SaveAs { get; set; } + public string Find { get; set; } + public string Replace { get; set; } + public string VisualSync { get; set; } + public string SpellCheck { get; set; } + public string SettingsName { get; set; } + public string Help { get; set; } + public string ShowFrameRate { get; set; } + public string DefaultFrameRate { get; set; } + public string DefaultFileEncoding { get; set; } + public string AutoDetectAnsiEncoding { get; set; } + public string SubtitleLineMaximumLength { get; set; } + public string MaximumCharactersPerSecond { get; set; } + public string AutoWrapWhileTyping { get; set; } + public string DurationMinimumMilliseconds { get; set; } + public string DurationMaximumMilliseconds { get; set; } + public string MinimumGapMilliseconds { get; set; } + public string SubtitleFont { get; set; } + public string SubtitleFontSize { get; set; } + public string SubtitleBold { get; set; } + public string SubtitleCenter { get; set; } + public string SubtitleFontColor { get; set; } + public string SubtitleBackgroundColor { get; set; } + public string SpellChecker { get; set; } + public string RememberRecentFiles { get; set; } + public string StartWithLastFileLoaded { get; set; } + public string RememberSelectedLine { get; set; } + public string RememberPositionAndSize { get; set; } + public string StartInSourceView { get; set; } + public string RemoveBlankLinesWhenOpening { get; set; } + public string ShowLineBreaksAs { get; set; } + public string MainListViewDoubleClickAction { get; set; } + public string MainListViewNothing { get; set; } + public string MainListViewVideoGoToPositionAndPause { get; set; } + public string MainListViewVideoGoToPositionAndPlay { get; set; } + public string MainListViewEditText { get; set; } + public string MainListViewVideoGoToPositionMinus1SecAndPause { get; set; } + public string MainListViewVideoGoToPositionMinusHalfSecAndPause { get; set; } + public string MainListViewVideoGoToPositionMinus1SecAndPlay { get; set; } + public string MainListViewEditTextAndPause { get; set; } + public string AutoBackup { get; set; } + public string AutoBackupEveryMinute { get; set; } + public string AutoBackupEveryFiveMinutes { get; set; } + public string AutoBackupEveryFifteenMinutes { get; set; } + public string CheckForUpdates { get; set; } + public string AllowEditOfOriginalSubtitle { get; set; } + public string PromptDeleteLines { get; set; } + public string TimeCodeMode { get; set; } + public string TimeCodeModeHHMMSSMS { get; set; } + public string TimeCodeModeHHMMSSFF { get; set; } + public string VideoEngine { get; set; } + public string DirectShow { get; set; } + public string DirectShowDescription { get; set; } + public string ManagedDirectX { get; set; } + public string ManagedDirectXDescription { get; set; } + public string MpcHc { get; set; } + public string MpcHcDescription { get; set; } + public string MPlayer { get; set; } + public string MPlayerDescription { get; set; } + public string VlcMediaPlayer { get; set; } + public string VlcMediaPlayerDescription { get; set; } + public string VlcBrowseToLabel { get; set; } + public string ShowStopButton { get; set; } + public string ShowMuteButton { get; set; } + public string ShowFullscreenButton { get; set; } + public string PreviewFontSize { get; set; } + public string MainWindowVideoControls { get; set; } + public string CustomSearchTextAndUrl { get; set; } + public string WaveformAppearance { get; set; } + public string WaveformGridColor { get; set; } + public string WaveformShowGridLines { get; set; } + public string ReverseMouseWheelScrollDirection { get; set; } + public string WaveformAllowOverlap { get; set; } + public string WaveformFocusMouseEnter { get; set; } + public string WaveformListViewFocusMouseEnter { get; set; } + public string WaveformBorderHitMs1 { get; set; } + public string WaveformBorderHitMs2 { get; set; } + public string WaveformColor { get; set; } + public string WaveformSelectedColor { get; set; } + public string WaveformBackgroundColor { get; set; } + public string WaveformTextColor { get; set; } + public string WaveformTextFontSize { get; set; } + public string WaveformAndSpectrogramsFolderEmpty { get; set; } + public string WaveformAndSpectrogramsFolderInfo { get; set; } + public string Spectrogram { get; set; } + public string GenerateSpectrogram { get; set; } + public string SpectrogramAppearance { get; set; } + public string SpectrogramOneColorGradient { get; set; } + public string SpectrogramClassic { get; set; } + public string WaveformUseFFmpeg { get; set; } + public string WaveformFFmpegPath { get; set; } + public string WaveformBrowseToFFmpeg { get; set; } + public string WaveformBrowseToVLC { get; set; } + public string SubStationAlphaStyle { get; set; } + public string ChooseFont { get; set; } + public string ChooseColor { get; set; } + public string SsaOutline { get; set; } + public string SsaShadow { get; set; } + public string SsaOpaqueBox { get; set; } + public string Testing123 { get; set; } + public string Language { get; set; } + public string NamesIgnoreLists { get; set; } + public string AddNameEtc { get; set; } + public string AddWord { get; set; } + public string Remove { get; set; } + public string AddPair { get; set; } + public string UserWordList { get; set; } + public string OcrFixList { get; set; } + public string Location { get; set; } + public string UseOnlineNamesEtc { get; set; } + public string WordAddedX { get; set; } + public string WordAlreadyExists { get; set; } + public string WordNotFound { get; set; } + public string RemoveX { get; set; } + public string CannotUpdateNamesEtcOnline { get; set; } + public string ProxyServerSettings { get; set; } + public string ProxyAddress { get; set; } + public string ProxyAuthentication { get; set; } + public string ProxyUserName { get; set; } + public string ProxyPassword { get; set; } + public string ProxyDomain { get; set; } + public string NetworkSessionSettings { get; set; } + public string NetworkSessionNewSound { get; set; } + public string PlayXSecondsAndBack { get; set; } + public string StartSceneIndex { get; set; } + public string EndSceneIndex { get; set; } + public string FirstPlusX { get; set; } + public string LastMinusX { get; set; } + public string FixCommonerrors { get; set; } + public string MergeLinesShorterThan { get; set; } + public string MusicSymbol { get; set; } + public string MusicSymbolsToReplace { get; set; } + public string FixCommonOcrErrorsUseHardcodedRules { get; set; } + public string FixCommonerrorsFixShortDisplayTimesAllowMoveStartTime { get; set; } + public string Shortcuts { get; set; } + public string Shortcut { get; set; } + public string Control { get; set; } + public string Alt { get; set; } + public string Shift { get; set; } + public string Key { get; set; } + public string TextBox { get; set; } + public string UpdateShortcut { get; set; } + public string ShortcutIsNotValid { get; set; } + public string ToggleDockUndockOfVideoControls { get; set; } + public string CreateSetEndAddNewAndGoToNew { get; set; } + public string AdjustViaEndAutoStartAndGoToNext { get; set; } + public string AdjustSetEndTimeAndGoToNext { get; set; } + public string AdjustSetStartAutoDurationAndGoToNext { get; set; } + public string AdjustSetEndNextStartAndGoToNext { get; set; } + public string AdjustStartDownEndUpAndGoToNext { get; set; } + public string AdjustSelected100MsForward { get; set; } + public string AdjustSelected100MsBack { get; set; } + public string AdjustSetStartTimeKeepDuration { get; set; } + public string AdjustSetEndAndOffsetTheRest { get; set; } + public string AdjustSetEndAndOffsetTheRestAndGoToNext { get; set; } + public string MainCreateStartDownEndUp { get; set; } + public string MergeDialog { get; set; } + public string GoToNext { get; set; } + public string GoToPrevious { get; set; } + public string GoToCurrentSubtitleStart { get; set; } + public string GoToCurrentSubtitleEnd { get; set; } + public string ToggleFocus { get; set; } + public string ToggleDialogDashes { get; set; } + public string Alignment { get; set; } + public string CopyTextOnly { get; set; } + public string CopyTextOnlyFromOriginalToCurrent { get; set; } + public string AutoDurationSelectedLines { get; set; } + public string ReverseStartAndEndingForRTL { get; set; } + public string VerticalZoom { get; set; } + public string VerticalZoomOut { get; set; } + public string WaveformSeekSilenceForward { get; set; } + public string WaveformSeekSilenceBack { get; set; } + public string WaveformAddTextHere { get; set; } + public string WaveformPlayNewSelection { get; set; } + public string WaveformPlayFirstSelectedSubtitle { get; set; } + public string WaveformFocusListView { get; set; } + public string GoBack1Frame { get; set; } + public string GoForward1Frame { get; set; } + public string GoBack100Milliseconds { get; set; } + public string GoForward100Milliseconds { get; set; } + public string GoBack500Milliseconds { get; set; } + public string GoForward500Milliseconds { get; set; } + public string GoBack1Second { get; set; } + public string GoForward1Second { get; set; } + public string TogglePlayPause { get; set; } + public string Pause { get; set; } + public string Fullscreen { get; set; } + public string CustomSearch1 { get; set; } + public string CustomSearch2 { get; set; } + public string CustomSearch3 { get; set; } + public string CustomSearch4 { get; set; } + public string CustomSearch5 { get; set; } + public string CustomSearch6 { get; set; } + public string SyntaxColoring { get; set; } + public string ListViewSyntaxColoring { get; set; } + public string SyntaxColorDurationIfTooSmall { get; set; } + public string SyntaxColorDurationIfTooLarge { get; set; } + public string SyntaxColorTextIfTooLong { get; set; } + public string SyntaxColorTextMoreThanXLines { get; set; } + public string SyntaxColorOverlap { get; set; } + public string SyntaxErrorColor { get; set; } + public string GoToFirstSelectedLine { get; set; } + public string GoToNextEmptyLine { get; set; } + public string MergeSelectedLines { get; set; } + public string MergeSelectedLinesOnlyFirstText { get; set; } + public string ToggleTranslationMode { get; set; } + public string SwitchOriginalAndTranslation { get; set; } + public string MergeOriginalAndTranslation { get; set; } + public string ShortcutIsAlreadyDefinedX { get; set; } + public string ToggleTranslationAndOriginalInPreviews { get; set; } + public string ListViewColumnDelete { get; set; } + public string ListViewColumnInsert { get; set; } + public string ListViewColumnPaste { get; set; } + public string ListViewFocusWaveform { get; set; } + public string ListViewGoToNextError { get; set; } + public string ShowBeamer { get; set; } + public string MainTextBoxMoveLastWordDown { get; set; } + public string MainTextBoxMoveFirstWordFromNextUp { get; set; } + public string MainTextBoxSelectionToLower { get; set; } + public string MainTextBoxSelectionToUpper { get; set; } + public string MainTextBoxToggleAutoDuration { get; set; } + public string MainTextBoxAutoBreak { get; set; } + public string MainTextBoxUnbreak { get; set; } + public string MainFileSaveAll { get; set; } + public string Miscellaneous { get; set; } + public string UseDoNotBreakAfterList { get; set; } + } + + public class SetVideoOffset + { + public string Title { get; set; } + public string Description { get; set; } + public string RelativeToCurrentVideoPosition { get; set; } + } + + public class ShowEarlierLater + { + public string Title { get; set; } + public string TitleAll { get; set; } + public string ShowEarlier { get; set; } + public string ShowLater { get; set; } + public string TotalAdjustmentX { get; set; } + public string AllLines { get; set; } + public string SelectedLinesOnly { get; set; } + public string SelectedLinesAndForward { get; set; } + } + + public class ShowHistory + { + public string Title { get; set; } + public string SelectRollbackPoint { get; set; } + public string Time { get; set; } + public string Description { get; set; } + public string CompareHistoryItems { get; set; } + public string CompareWithCurrent { get; set; } + public string Rollback { get; set; } + } + + public class SpellCheck + { + public string Title { get; set; } + public string FullText { get; set; } + public string WordNotFound { get; set; } + public string Language { get; set; } + public string Change { get; set; } + public string ChangeAll { get; set; } + public string SkipOnce { get; set; } + public string SkipAll { get; set; } + public string AddToUserDictionary { get; set; } + public string AddToNamesAndIgnoreList { get; set; } + public string AddToOcrReplaceList { get; set; } + public string Abort { get; set; } + public string Use { get; set; } + public string UseAlways { get; set; } + public string Suggestions { get; set; } + public string SpellCheckProgress { get; set; } + public string EditWholeText { get; set; } + public string EditWordOnly { get; set; } + public string AddXToNamesEtc { get; set; } + public string AutoFixNames { get; set; } + public string CheckOneLetterWords { get; set; } + public string TreatINQuoteAsING { get; set; } + public string ImageText { get; set; } + public string SpellCheckCompleted { get; set; } + public string SpellCheckAborted { get; set; } + public string UndoX { get; set; } + } + + public class Split + { + public string Title { get; set; } + public string SplitOptions { get; set; } + public string Lines { get; set; } + public string Characters { get; set; } + public string NumberOfEqualParts { get; set; } + public string SubtitleInfo { get; set; } + public string NumberOfLinesX { get; set; } + public string NumberOfCharactersX { get; set; } + public string Output { get; set; } + public string FileName { get; set; } + public string OutputFolder { get; set; } + public string DoSplit { get; set; } + public string Basic { get; set; } + } + + public class SplitLongLines + { + public string Title { get; set; } + public string SingleLineMaximumLength { get; set; } + public string LineMaximumLength { get; set; } + public string LineContinuationBeginEndStrings { get; set; } + public string NumberOfSplits { get; set; } + public string LongestSingleLineIsXAtY { get; set; } + public string LongestLineIsXAtY { get; set; } + } + + public class SplitSubtitle + { + public string Title { get; set; } + public string Description1 { get; set; } + public string Description2 { get; set; } + public string Split { get; set; } + public string Done { get; set; } + public string NothingToSplit { get; set; } + public string SavePartOneAs { get; set; } + public string SavePartTwoAs { get; set; } + public string Part1 { get; set; } + public string Part2 { get; set; } + public string UnableToSaveFileX { get; set; } + public string OverwriteExistingFiles { get; set; } + public string FolderNotFoundX { get; set; } + public string Untitled { get; set; } + } + + public class StartNumberingFrom + { + public string Title { get; set; } + public string StartFromNumber { get; set; } + public string PleaseEnterAValidNumber { get; set; } + } + + public class Statistics + { + public string Title { get; set; } + public string TitleWithFileName { get; set; } + public string GeneralStatistics { get; set; } + public string MostUsed { get; set; } + public string MostUsedLines { get; set; } + public string MostUsedWords { get; set; } + public string NothingFound { get; set; } + public string NumberOfLinesX { get; set; } + public string LengthInFormatXinCharactersY { get; set; } + public string NumberOfCharactersInTextOnly { get; set; } + public string TotalCharsPerSecond { get; set; } + public string NumberOfItalicTags { get; set; } + public string NumberOfBoldTags { get; set; } + public string NumberOfUnderlineTags { get; set; } + public string NumberOfFontTags { get; set; } + public string NumberOfAlignmentTags { get; set; } + public string LineLengthMinimum { get; set; } + public string LineLengthMaximum { get; set; } + public string LineLengthAverage { get; set; } + public string LinesPerSubtitleAverage { get; set; } + public string SingleLineLengthMinimum { get; set; } + public string SingleLineLengthMaximum { get; set; } + public string SingleLineLengthAverage { get; set; } + public string DurationMinimum { get; set; } + public string DurationMaximum { get; set; } + public string DurationAverage { get; set; } + public string CharactersPerSecondMinimum { get; set; } + public string CharactersPerSecondMaximum { get; set; } + public string CharactersPerSecondAverage { get; set; } + public string Export { get; set; } + } + + public class SubStationAlphaProperties + { + public string Title { get; set; } + public string TitleSubstationAlpha { get; set; } + public string Script { get; set; } + public string ScriptTitle { get; set; } + public string OriginalScript { get; set; } + public string Translation { get; set; } + public string Editing { get; set; } + public string Timing { get; set; } + public string SyncPoint { get; set; } + public string UpdatedBy { get; set; } + public string UpdateDetails { get; set; } + public string Resolution { get; set; } + public string VideoResolution { get; set; } + public string Options { get; set; } + public string WrapStyle { get; set; } + public string Collision { get; set; } + public string ScaleBorderAndShadow { get; set; } + } + + public class SubStationAlphaStyles + { + public string Title { get; set; } + public string TitleSubstationAlpha { get; set; } + public string Styles { get; set; } + public string Properties { get; set; } + public string Name { get; set; } + public string Font { get; set; } + public string FontName { get; set; } + public string FontSize { get; set; } + public string UseCount { get; set; } + public string Primary { get; set; } + public string Secondary { get; set; } + public string Tertiary { get; set; } + public string Outline { get; set; } + public string Shadow { get; set; } + public string Back { get; set; } + public string Alignment { get; set; } + public string TopLeft { get; set; } + public string TopCenter { get; set; } + public string TopRight { get; set; } + public string MiddleLeft { get; set; } + public string MiddleCenter { get; set; } + public string MiddleRight { get; set; } + public string BottomLeft { get; set; } + public string BottomCenter { get; set; } + public string BottomRight { get; set; } + public string Colors { get; set; } + public string Margins { get; set; } + public string MarginLeft { get; set; } + public string MarginRight { get; set; } + public string MarginVertical { get; set; } + public string Border { get; set; } + public string PlusShadow { get; set; } + public string OpaqueBox { get; set; } + public string Import { get; set; } + public string Export { get; set; } + public string Copy { get; set; } + public string CopyOfY { get; set; } + public string CopyXOfY { get; set; } + public string New { get; set; } + public string Remove { get; set; } + public string RemoveAll { get; set; } + public string ImportStyleFromFile { get; set; } + public string ExportStyleToFile { get; set; } + public string ChooseStyle { get; set; } + public string StyleAlreadyExits { get; set; } + public string StyleXExportedToFileY { get; set; } + public string StyleXImportedFromFileY { get; set; } + } + + public class PointSync + { + public string Title { get; set; } + public string TitleViaOtherSubtitle { get; set; } + public string SyncHelp { get; set; } + public string SetSyncPoint { get; set; } + public string RemoveSyncPoint { get; set; } + public string SyncPointsX { get; set; } + public string Info { get; set; } + public string ApplySync { get; set; } + } + + public class TransportStreamSubtitleChooser + { + public string Title { get; set; } + public string PidLine { get; set; } + public string SubLine { get; set; } + } + + public class UnknownSubtitle + { + public string Title { get; set; } + public string Message { get; set; } + } + + public class VisualSync + { + public string Title { get; set; } + public string StartScene { get; set; } + public string EndScene { get; set; } + public string Synchronize { get; set; } + public string HalfASecondBack { get; set; } + public string ThreeSecondsBack { get; set; } + public string PlayXSecondsAndBack { get; set; } + public string FindText { get; set; } + public string GoToSubPosition { get; set; } + public string KeepChangesTitle { get; set; } + public string KeepChangesMessage { get; set; } + public string SynchronizationDone { get; set; } + public string StartSceneMustComeBeforeEndScene { get; set; } + public string Tip { get; set; } + } + + public class VobSubEditCharacters + { + public string Title { get; set; } + public string ChooseCharacter { get; set; } + public string ImageCompareFiles { get; set; } + public string CurrentCompareImage { get; set; } + public string TextAssociatedWithImage { get; set; } + public string IsItalic { get; set; } + public string Update { get; set; } + public string Delete { get; set; } + public string ImageDoubleSize { get; set; } + public string ImageFileNotFound { get; set; } + public string Image { get; set; } + } + + public class VobSubOcr + { + public string Title { get; set; } + public string TitleBluRay { get; set; } + public string OcrMethod { get; set; } + public string OcrViaModi { get; set; } + public string OcrViaTesseract { get; set; } + public string OcrViaNOCR { get; set; } + public string Language { get; set; } + public string OcrViaImageCompare { get; set; } + public string ImageDatabase { get; set; } + public string NoOfPixelsIsSpace { get; set; } + public string MaxErrorPercent { get; set; } + public string New { get; set; } + public string Edit { get; set; } + public string StartOcr { get; set; } + public string Stop { get; set; } + public string StartOcrFrom { get; set; } + public string LoadingVobSubImages { get; set; } + public string LoadingImageCompareDatabase { get; set; } + public string ConvertingImageCompareDatabase { get; set; } + public string SubtitleImage { get; set; } + public string SubtitleText { get; set; } + public string UnableToCreateCharacterDatabaseFolder { get; set; } + public string SubtitleImageXofY { get; set; } + public string ImagePalette { get; set; } + public string UseCustomColors { get; set; } + public string Transparent { get; set; } + public string TransparentMinAlpha { get; set; } + public string TransportStream { get; set; } + public string TransportStreamGrayscale { get; set; } + public string TransportStreamGetColor { get; set; } + public string PromptForUnknownWords { get; set; } + public string TryToGuessUnkownWords { get; set; } + public string AutoBreakSubtitleIfMoreThanTwoLines { get; set; } + public string AllFixes { get; set; } + public string GuessesUsed { get; set; } + public string UnknownWords { get; set; } + public string OcrAutoCorrectionSpellChecking { get; set; } + public string FixOcrErrors { get; set; } + public string ImportTextWithMatchingTimeCodes { get; set; } + public string ImportNewTimeCodes { get; set; } + public string SaveSubtitleImageAs { get; set; } + public string SaveAllSubtitleImagesAsBdnXml { get; set; } + public string SaveAllSubtitleImagesWithHtml { get; set; } + public string XImagesSavedInY { get; set; } + public string TryModiForUnknownWords { get; set; } + public string DictionaryX { get; set; } + public string RightToLeft { get; set; } + public string ShowOnlyForcedSubtitles { get; set; } + public string UseTimeCodesFromIdx { get; set; } + public string NoMatch { get; set; } + public string AutoTransparentBackground { get; set; } + public string InspectCompareMatchesForCurrentImage { get; set; } + public string EditLastAdditions { get; set; } + public string SetUnitalicFactor { get; set; } + public string DiscardTitle { get; set; } + public string DiscardText { get; set; } + } + + public class VobSubOcrCharacter + { + public string Title { get; set; } + public string ShrinkSelection { get; set; } + public string ExpandSelection { get; set; } + public string SubtitleImage { get; set; } + public string Characters { get; set; } + public string CharactersAsText { get; set; } + public string Italic { get; set; } + public string Abort { get; set; } + public string Skip { get; set; } + public string Nordic { get; set; } + public string Spanish { get; set; } + public string German { get; set; } + public string AutoSubmitOnFirstChar { get; set; } + public string EditLastX { get; set; } + } + + public class VobSubOcrCharacterInspect + { + public string Title { get; set; } + public string InspectItems { get; set; } + public string AddBetterMatch { get; set; } + } + + public class VobSubOcrNewFolder + { + public string Title { get; set; } + public string Message { get; set; } + } + + public class VobSubOcrSetItalicFactor + { + public string Title { get; set; } + public string Description { get; set; } + } + + public class Waveform + { + public string ClickToAddWaveform { get; set; } + public string ClickToAddWaveformAndSpectrogram { get; set; } + public string Seconds { get; set; } + public string ZoomIn { get; set; } + public string ZoomOut { get; set; } + public string AddParagraphHere { get; set; } + public string AddParagraphHereAndPasteText { get; set; } + public string FocusTextBox { get; set; } + public string DeleteParagraph { get; set; } + public string Split { get; set; } + public string SplitAtCursor { get; set; } + public string MergeWithPrevious { get; set; } + public string MergeWithNext { get; set; } + public string PlaySelection { get; set; } + public string ShowWaveformAndSpectrogram { get; set; } + public string ShowWaveformOnly { get; set; } + public string ShowSpectrogramOnly { get; set; } + public string GuessTimeCodes { get; set; } + public string SeekSilence { get; set; } + } + + public class WaveformGenerateTimeCodes + { + public string Title { get; set; } + public string StartFrom { get; set; } + public string CurrentVideoPosition { get; set; } + public string Beginning { get; set; } + public string DeleteLines { get; set; } + public string FromCurrentVideoPosition { get; set; } + public string DetectOptions { get; set; } + public string ScanBlocksOfMs { get; set; } + public string BlockAverageVolMin1 { get; set; } + public string BlockAverageVolMin2 { get; set; } + public string BlockAverageVolMax1 { get; set; } + public string BlockAverageVolMax2 { get; set; } + public string SplitLongLinesAt1 { get; set; } + public string SplitLongLinesAt2 { get; set; } + public string Other { get; set; } + } + + public class WebVttNewVoice + { + public string Title { get; set; } + public string VoiceName { get; set; } + } + + } +} \ No newline at end of file diff --git a/libse/LibSE.csproj b/libse/LibSE.csproj new file mode 100644 index 000000000..d6a9a57cf --- /dev/null +++ b/libse/LibSE.csproj @@ -0,0 +1,447 @@ + + + + + Debug + AnyCPU + {3E3CB28F-3A7B-430F-9EB3-0D6C1E53B753} + Library + Properties + Nikse.SubtitleEdit.Core + libse + v4.0 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libse/ManagedBitmap.cs b/libse/ManagedBitmap.cs new file mode 100644 index 000000000..e317345d0 --- /dev/null +++ b/libse/ManagedBitmap.cs @@ -0,0 +1,211 @@ +using System.Drawing; +using System.IO; +using System.IO.Compression; + +namespace Nikse.SubtitleEdit.Core +{ + public class ManagedBitmap + { + public int Width { get; private set; } + public int Height { get; private set; } + + private Color[] _colors; + public bool LoadedOk { get; private set; } + + public ManagedBitmap(string fileName) + { + try + { + byte[] buffer = new byte[1024]; + MemoryStream fd = new MemoryStream(); + Stream fs = File.OpenRead(fileName); + using (Stream csStream = new GZipStream(fs, CompressionMode.Decompress)) + { + int nRead; + while ((nRead = csStream.Read(buffer, 0, buffer.Length)) > 0) + { + fd.Write(buffer, 0, nRead); + } + csStream.Flush(); + buffer = fd.ToArray(); + } + + Width = buffer[4] << 8 | buffer[5]; + Height = buffer[6] << 8 | buffer[7]; + _colors = new Color[Width * Height]; + int start = 8; + for (int i = 0; i < _colors.Length; i++) + { + _colors[i] = Color.FromArgb(buffer[start], buffer[start + 1], buffer[start + 2], buffer[start + 3]); + start += 4; + } + } + catch + { + LoadedOk = false; + } + } + + public ManagedBitmap(Stream stream) + { + byte[] buffer = new byte[8]; + stream.Read(buffer, 0, buffer.Length); + Width = buffer[4] << 8 | buffer[5]; + Height = buffer[6] << 8 | buffer[7]; + _colors = new Color[Width * Height]; + buffer = new byte[Width * Height * 4]; + stream.Read(buffer, 0, buffer.Length); + int start = 0; + for (int i = 0; i < _colors.Length; i++) + { + _colors[i] = Color.FromArgb(buffer[start], buffer[start + 1], buffer[start + 2], buffer[start + 3]); + start += 4; + } + } + + public ManagedBitmap(Bitmap oldBitmap) + { + NikseBitmap nbmp = new NikseBitmap(oldBitmap); + Width = nbmp.Width; + Height = nbmp.Height; + _colors = new Color[Width * Height]; + for (int y = 0; y < Height; y++) + { + for (int x = 0; x < Width; x++) + { + this.SetPixel(x, y, nbmp.GetPixel(x, y)); + } + } + } + + public ManagedBitmap(NikseBitmap nbmp) + { + Width = nbmp.Width; + Height = nbmp.Height; + _colors = new Color[Width * Height]; + for (int y = 0; y < Height; y++) + { + for (int x = 0; x < Width; x++) + { + this.SetPixel(x, y, nbmp.GetPixel(x, y)); + } + } + } + + public void Save(string fileName) + { + using (MemoryStream outFile = new MemoryStream()) + { + byte[] buffer = System.Text.Encoding.UTF8.GetBytes("MBMP"); + outFile.Write(buffer, 0, buffer.Length); + WriteInt16(outFile, (short)Width); + WriteInt16(outFile, (short)Height); + foreach (Color c in _colors) + { + WriteColor(outFile, c); + } + buffer = outFile.ToArray(); + using (GZipStream gz = new GZipStream(new FileStream(fileName, FileMode.Create), CompressionMode.Compress, false)) + { + gz.Write(buffer, 0, buffer.Length); + } + } + } + + public void AppendToStream(Stream targetStream) + { + using (MemoryStream outFile = new MemoryStream()) + { + byte[] buffer = System.Text.Encoding.UTF8.GetBytes("MBMP"); + outFile.Write(buffer, 0, buffer.Length); + WriteInt16(outFile, (short)Width); + WriteInt16(outFile, (short)Height); + foreach (Color c in _colors) + { + WriteColor(outFile, c); + } + buffer = outFile.ToArray(); + targetStream.Write(buffer, 0, buffer.Length); + } + } + + //private static int ReadInt16(Stream stream) + //{ + // byte b0 = (byte)stream.ReadByte(); + // byte b1 = (byte)stream.ReadByte(); + // return b0 << 8 | b1; + //} + + private static void WriteInt16(Stream stream, short val) + { + byte[] buffer = new byte[2]; + buffer[0] = (byte)((val & 0xFF00) >> 8); + buffer[1] = (byte)(val & 0x00FF); + stream.Write(buffer, 0, buffer.Length); + } + + private static void WriteColor(Stream stream, Color c) + { + byte[] buffer = new byte[4]; + buffer[0] = (byte)c.A; + buffer[1] = (byte)c.R; + buffer[2] = (byte)c.G; + buffer[3] = (byte)c.B; + stream.Write(buffer, 0, buffer.Length); + } + + public ManagedBitmap(int width, int height) + { + Width = width; + Height = height; + _colors = new Color[Width * Height]; + } + + public Color GetPixel(int x, int y) + { + return _colors[Width * y + x]; + } + + public void SetPixel(int x, int y, Color c) + { + _colors[Width * y + x] = c; + } + + /// + /// Copies a rectangle from the bitmap to a new bitmap + /// + /// Source rectangle + /// Rectangle from current image as new bitmap + public ManagedBitmap GetRectangle(Rectangle section) + { + ManagedBitmap newRectangle = new ManagedBitmap(section.Width, section.Height); + + int recty = 0; + for (int y = section.Top; y < section.Top + section.Height; y++) + { + int rectx = 0; + for (int x = section.Left; x < section.Left + section.Width; x++) + { + newRectangle.SetPixel(rectx, recty, this.GetPixel(x, y)); + rectx++; + } + recty++; + } + return newRectangle; + } + + public Bitmap ToOldBitmap() + { + NikseBitmap nbmp = new NikseBitmap(Width, Height); + for (int y = 0; y < Height; y++) + { + for (int x = 0; x < Width; x++) + { + nbmp.SetPixel(x, y, this.GetPixel(x, y)); + } + } + return nbmp.GetBitmap(); + } + + } +} \ No newline at end of file diff --git a/libse/MurMurHash3.cs b/libse/MurMurHash3.cs new file mode 100644 index 000000000..5675a8c80 --- /dev/null +++ b/libse/MurMurHash3.cs @@ -0,0 +1,88 @@ +namespace Nikse.SubtitleEdit.Core +{ + // The MurmurHash3 algorithm was created by Austin Appleby and put into the public domain. See http://code.google.com/p/smhasher + // This code is based on https://gist.github.com/automatonic/3725443 + public class MurMurHash3 + { + private const uint seed = 144; + + /// + /// Fast hashing of byte array + /// + /// Byte array to hash + /// Hash value + public static uint Hash(byte[] arr) + { + const uint c1 = 0xcc9e2d51; + const uint c2 = 0x1b873593; + uint h1 = seed; + uint k1 = 0; + + int length = arr.Length / 4; + for (int i = 0; i < length; i++) + { + int idx = i * 4; + k1 = (uint)(arr[idx] | arr[idx + 1] << 8 | arr[idx + 2] << 16 | arr[idx + 3] << 24); + + // bitmagic hash + k1 *= c1; + k1 = rotl32(k1, 15); + k1 *= c2; + + h1 ^= k1; + h1 = rotl32(h1, 13); + h1 = h1 * 5 + 0xe6546b64; + } + + switch (arr.Length % 4) + { + case 3: + k1 = (uint)(arr[arr.Length - 3] | arr[arr.Length - 2] << 8 | arr[arr.Length - 1] << 16); + k1 *= c1; + k1 = rotl32(k1, 15); + k1 *= c2; + h1 ^= k1; + break; + case 2: + k1 = (uint)(arr[arr.Length - 2] | arr[arr.Length - 1] << 8); + k1 *= c1; + k1 = rotl32(k1, 15); + k1 *= c2; + h1 ^= k1; + break; + case 1: + k1 = (uint)(arr[arr.Length - 1]); + k1 *= c1; + k1 = rotl32(k1, 15); + k1 *= c2; + h1 ^= k1; + break; + } + + // finalization, magic chants to wrap it all up + h1 ^= (uint)arr.Length; + h1 = fmix(h1); + + unchecked //ignore overflow + { + return h1; + } + } + + private static uint rotl32(uint x, byte r) + { + return (x << r) | (x >> (32 - r)); + } + + private static uint fmix(uint h) + { + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + return h; + } + } + +} \ No newline at end of file diff --git a/libse/NativeMethods.cs b/libse/NativeMethods.cs new file mode 100644 index 000000000..4d3bc8eb4 --- /dev/null +++ b/libse/NativeMethods.cs @@ -0,0 +1,131 @@ +using System; +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core +{ + internal static class NativeMethods + { + + #region Hunspell + + [DllImport("libhunspell", SetLastError = true, CharSet = CharSet.Ansi, BestFitMapping = false)] + internal static extern IntPtr Hunspell_create(string affpath, string dpath); + + [DllImport("libhunspell")] + internal static extern IntPtr Hunspell_destroy(IntPtr hunspellHandle); + + [DllImport("libhunspell", SetLastError = true, CharSet = CharSet.Ansi, BestFitMapping = false)] + internal static extern int Hunspell_spell(IntPtr hunspellHandle, string word); + + [DllImport("libhunspell", SetLastError = true, CharSet = CharSet.Ansi, BestFitMapping = false)] + internal static extern int Hunspell_suggest(IntPtr hunspellHandle, IntPtr slst, string word); + + [DllImport("libhunspell")] + internal static extern void Hunspell_free_list(IntPtr hunspellHandle, IntPtr slst, int n); + + #endregion Hunspell + + #region Win32 API + + // Win32 API functions for dynamically loading DLLs + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi, BestFitMapping = false)] + internal static extern IntPtr LoadLibrary(string dllToLoad); + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi, BestFitMapping = false)] + internal static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName); + + [DllImport("kernel32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool FreeLibrary(IntPtr hModule); + + [DllImport("user32.dll")] + internal static extern short GetKeyState(int vKey); + + [DllImport("kernel32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool AttachConsole(int dwProcessId); + + [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool FreeConsole(); + + [DllImport("user32.dll", EntryPoint = "SetWindowPos")] + internal static extern IntPtr SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int y, int width, int height, int wFlags); + + #endregion Win32 API + + #region VLC + + // LibVLC Core - http://www.videolan.org/developers/vlc/doc/doxygen/html/group__libvlc__core.html + [DllImport("libvlc")] + internal static extern IntPtr libvlc_new(int argc, [MarshalAs(UnmanagedType.LPArray)] string[] argv); + + [DllImport("libvlc")] + internal static extern void libvlc_release(IntPtr libVlc); + + // LibVLC Media - http://www.videolan.org/developers/vlc/doc/doxygen/html/group__libvlc__media.html + [DllImport("libvlc")] + internal static extern IntPtr libvlc_media_new_path(IntPtr instance, byte[] input); + + [DllImport("libvlc")] + internal static extern IntPtr libvlc_media_player_new_from_media(IntPtr media); + + [DllImport("libvlc")] + internal static extern void libvlc_media_release(IntPtr media); + + // LibVLC Audio Controls - http://www.videolan.org/developers/vlc/doc/doxygen/html/group__libvlc__audio.html + [DllImport("libvlc")] + internal static extern int libvlc_audio_get_track_count(IntPtr mediaPlayer); + + [DllImport("libvlc")] + internal static extern int libvlc_audio_get_track(IntPtr mediaPlayer); + + [DllImport("libvlc")] + internal static extern int libvlc_audio_set_track(IntPtr mediaPlayer, int trackNumber); + + // LibVLC Audio Controls - http://www.videolan.org/developers/vlc/doc/doxygen/html/group__libvlc__audio.html + [DllImport("libvlc")] + internal static extern int libvlc_audio_get_volume(IntPtr mediaPlayer); + + [DllImport("libvlc")] + internal static extern void libvlc_audio_set_volume(IntPtr mediaPlayer, int volume); + + // LibVLC media player - http://www.videolan.org/developers/vlc/doc/doxygen/html/group__libvlc__media__player.html + [DllImport("libvlc")] + internal static extern void libvlc_media_player_play(IntPtr mediaPlayer); + + [DllImport("libvlc")] + internal static extern void libvlc_media_player_stop(IntPtr mediaPlayer); + + [DllImport("libvlc")] + internal static extern void libvlc_media_player_pause(IntPtr mediaPlayer); + + [DllImport("libvlc")] + internal static extern void libvlc_media_player_set_hwnd(IntPtr mediaPlayer, IntPtr windowsHandle); + + [DllImport("libvlc")] + internal static extern Int64 libvlc_media_player_get_time(IntPtr mediaPlayer); + + [DllImport("libvlc")] + internal static extern void libvlc_media_player_set_time(IntPtr mediaPlayer, Int64 position); + + [DllImport("libvlc")] + internal static extern byte libvlc_media_player_get_state(IntPtr mediaPlayer); + + [DllImport("libvlc")] + internal static extern Int64 libvlc_media_player_get_length(IntPtr mediaPlayer); + + [DllImport("libvlc")] + internal static extern void libvlc_media_list_player_release(IntPtr mediaPlayer); + + [DllImport("libvlc")] + internal static extern float libvlc_media_player_get_rate(IntPtr mediaPlayer); + + [DllImport("libvlc")] + internal static extern int libvlc_media_player_set_rate(IntPtr mediaPlayer, float rate); + + #endregion VLC + + } + +} diff --git a/libse/NikseBitmap.cs b/libse/NikseBitmap.cs new file mode 100644 index 000000000..b21fccc3e --- /dev/null +++ b/libse/NikseBitmap.cs @@ -0,0 +1,1122 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Runtime.InteropServices; +using Nikse.SubtitleEdit.Core.VobSub; + +namespace Nikse.SubtitleEdit.Core +{ + public class RunLengthTwoParts + { + public byte[] Buffer1 { get; set; } + public byte[] Buffer2 { get; set; } + public int Length { get { return Buffer1.Length + Buffer2.Length; } } + } + + public class NikseBitmap + { + public int Width { get; private set; } + public int Height { get; private set; } + + private byte[] _bitmapData; + private int _pixelAddress; + + public NikseBitmap(int width, int height) + { + Width = width; + Height = height; + _bitmapData = new byte[Width * Height * 4]; + } + + public NikseBitmap(int width, int height, byte[] bitmapData) + { + Width = width; + Height = height; + _bitmapData = bitmapData; + } + + public NikseBitmap(Bitmap inputBitmap) + { + if (inputBitmap == null) + return; + + Width = inputBitmap.Width; + Height = inputBitmap.Height; + + if (inputBitmap.PixelFormat != PixelFormat.Format32bppArgb) + { + var newBitmap = new Bitmap(inputBitmap.Width, inputBitmap.Height, PixelFormat.Format32bppArgb); + using (var gr = Graphics.FromImage(newBitmap)) + { + gr.DrawImage(inputBitmap, 0, 0); + } + inputBitmap.Dispose(); + inputBitmap = newBitmap; + } + + var bitmapData = inputBitmap.LockBits(new Rectangle(0, 0, Width, Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); + _bitmapData = new byte[bitmapData.Stride * Height]; + Marshal.Copy(bitmapData.Scan0, _bitmapData, 0, _bitmapData.Length); + inputBitmap.UnlockBits(bitmapData); + } + + public NikseBitmap(NikseBitmap input) + { + Width = input.Width; + Height = input.Height; + _bitmapData = new byte[input._bitmapData.Length]; + Buffer.BlockCopy(input._bitmapData, 0, _bitmapData, 0, _bitmapData.Length); + } + + public void ReplaceNotDarkWithWhite() + { + var buffer = new byte[3]; + buffer[0] = 255; + buffer[1] = 255; + buffer[2] = 255; + for (int i = 0; i < _bitmapData.Length; i += 4) + { + if (_bitmapData[i + 3] > 200 && // Alpha + _bitmapData[i + 2] + _bitmapData[i + 1] + _bitmapData[i] > 200) + Buffer.BlockCopy(buffer, 0, _bitmapData, i, 3); + } + } + + public void ReplaceYellowWithWhite() + { + var buffer = new byte[3]; + buffer[0] = 255; + buffer[1] = 255; + buffer[2] = 255; + for (int i = 0; i < _bitmapData.Length; i += 4) + { + if (_bitmapData[i + 3] > 200 && // Alpha + _bitmapData[i + 2] > 199 && // Red + _bitmapData[i + 1] > 190 && // Green + _bitmapData[i] < 40) // Blue + Buffer.BlockCopy(buffer, 0, _bitmapData, i, 3); + } + } + + public void ReplaceNonWhiteWithTransparent() + { + var buffer = new byte[4]; + buffer[0] = 0; // B + buffer[1] = 0; // G + buffer[2] = 0; // R + buffer[3] = 0; // A + for (int i = 0; i < _bitmapData.Length; i += 4) + { + if (_bitmapData[i + 2] + _bitmapData[i + 1] + _bitmapData[i] < 300) + Buffer.BlockCopy(buffer, 0, _bitmapData, i, 4); + } + } + + public void ReplaceTransparentWith(Color c) + { + var buffer = new byte[4]; + buffer[0] = c.B; + buffer[1] = c.G; + buffer[2] = c.R; + buffer[3] = c.A; + for (int i = 0; i < _bitmapData.Length; i += 4) + { + if (_bitmapData[i + 3] < 10) + Buffer.BlockCopy(buffer, 0, _bitmapData, i, 4); + } + } + + public void MakeOneColor(Color c) + { + var buffer = new byte[4]; + buffer[0] = c.B; + buffer[1] = c.G; + buffer[2] = c.R; + buffer[3] = c.A; + + var bufferTransparent = new byte[4]; + bufferTransparent[0] = 0; + bufferTransparent[1] = 0; + bufferTransparent[2] = 0; + bufferTransparent[3] = 0; + for (int i = 0; i < _bitmapData.Length; i += 4) + { + if (_bitmapData[i] > 20) + Buffer.BlockCopy(buffer, 0, _bitmapData, i, 4); + else + Buffer.BlockCopy(bufferTransparent, 0, _bitmapData, i, 4); + } + } + + public int MakeOneColorRemoverOthers(Color c1, Color c2, int maxDif) + { + var buffer1 = new byte[4]; + buffer1[0] = c1.B; + buffer1[1] = c1.G; + buffer1[2] = c1.R; + buffer1[3] = c1.A; + + var buffer2 = new byte[4]; + buffer2[0] = c2.B; + buffer2[1] = c2.G; + buffer2[2] = c2.R; + buffer2[3] = c2.A; + + var bufferTransparent = new byte[4]; + bufferTransparent[0] = 0; + bufferTransparent[1] = 0; + bufferTransparent[2] = 0; + bufferTransparent[3] = 0; + int count = 0; + for (int i = 0; i < _bitmapData.Length; i += 4) + { + if (_bitmapData[i + 3] > 20) + { + if ((Math.Abs(buffer1[0] - _bitmapData[i]) < maxDif && + Math.Abs(buffer1[1] - _bitmapData[i + 1]) < maxDif && + Math.Abs(buffer1[2] - _bitmapData[i + 2]) < maxDif) || + (Math.Abs(buffer2[0] - _bitmapData[i]) < maxDif && + Math.Abs(buffer2[1] - _bitmapData[i + 1]) < maxDif && + Math.Abs(buffer2[2] - _bitmapData[i + 2]) < maxDif)) + { + count++; + } + else + { + Buffer.BlockCopy(bufferTransparent, 0, _bitmapData, i, 4); + } + } + else + { + Buffer.BlockCopy(bufferTransparent, 0, _bitmapData, i, 4); + } + } + return count; + } + + private static Color GetOutlineColor(Color borderColor) + { + if (borderColor.R + borderColor.G + borderColor.B < 30) + return Color.FromArgb(200, 75, 75, 75); + return Color.FromArgb(150, borderColor.R, borderColor.G, borderColor.B); + } + + /// + /// Convert a x-color image to four colors, for e.g. DVD sub pictures. + /// + /// Background color + /// Pattern color, normally white or yellow + /// Emphasis 1, normally black or near black (border) + /// + public Color ConverToFourColors(Color background, Color pattern, Color emphasis1, bool useInnerAntialize) + { + var backgroundBuffer = new byte[4]; + backgroundBuffer[0] = background.B; + backgroundBuffer[1] = background.G; + backgroundBuffer[2] = background.R; + backgroundBuffer[3] = background.A; + + var patternBuffer = new byte[4]; + patternBuffer[0] = pattern.B; + patternBuffer[1] = pattern.G; + patternBuffer[2] = pattern.R; + patternBuffer[3] = pattern.A; + + var emphasis1Buffer = new byte[4]; + emphasis1Buffer[0] = emphasis1.B; + emphasis1Buffer[1] = emphasis1.G; + emphasis1Buffer[2] = emphasis1.R; + emphasis1Buffer[3] = emphasis1.A; + + var emphasis2Buffer = new byte[4]; + var emphasis2 = GetOutlineColor(emphasis1); + if (!useInnerAntialize) + { + emphasis2Buffer[0] = emphasis2.B; + emphasis2Buffer[1] = emphasis2.G; + emphasis2Buffer[2] = emphasis2.R; + emphasis2Buffer[3] = emphasis2.A; + } + + for (int i = 0; i < _bitmapData.Length; i += 4) + { + int smallestDiff = 10000; + byte[] buffer = backgroundBuffer; + if (backgroundBuffer[3] == 0 && _bitmapData[i + 3] < 10) // transparent + { + } + else + { + int patternDiff = Math.Abs(patternBuffer[0] - _bitmapData[i]) + Math.Abs(patternBuffer[1] - _bitmapData[i + 1]) + Math.Abs(patternBuffer[2] - _bitmapData[i + 2]) + Math.Abs(patternBuffer[3] - _bitmapData[i + 3]); + if (patternDiff < smallestDiff) + { + smallestDiff = patternDiff; + buffer = patternBuffer; + } + + int emphasis1Diff = Math.Abs(emphasis1Buffer[0] - _bitmapData[i]) + Math.Abs(emphasis1Buffer[1] - _bitmapData[i + 1]) + Math.Abs(emphasis1Buffer[2] - _bitmapData[i + 2]) + Math.Abs(emphasis1Buffer[3] - _bitmapData[i + 3]); + if (useInnerAntialize) + { + if (emphasis1Diff - 20 < smallestDiff) + { + buffer = emphasis1Buffer; + } + } + else + { + if (emphasis1Diff < smallestDiff) + { + smallestDiff = emphasis1Diff; + buffer = emphasis1Buffer; + } + + int emphasis2Diff = Math.Abs(emphasis2Buffer[0] - _bitmapData[i]) + Math.Abs(emphasis2Buffer[1] - _bitmapData[i + 1]) + Math.Abs(emphasis2Buffer[2] - _bitmapData[i + 2]) + Math.Abs(emphasis2Buffer[3] - _bitmapData[i + 3]); + if (emphasis2Diff < smallestDiff) + { + buffer = emphasis2Buffer; + } + else if (_bitmapData[i + 3] >= 10 && _bitmapData[i + 3] < 90) // anti-alias + { + buffer = emphasis2Buffer; + } + } + } + Buffer.BlockCopy(buffer, 0, _bitmapData, i, 4); + } + + if (useInnerAntialize) + return VobSubAntialize(pattern, emphasis1); + + return emphasis2; + } + + private Color VobSubAntialize(Color pattern, Color emphasis1) + { + int r = (int)Math.Round(((pattern.R * 2.0 + emphasis1.R) / 3.0)); + int g = (int)Math.Round(((pattern.G * 2.0 + emphasis1.G) / 3.0)); + int b = (int)Math.Round(((pattern.B * 2.0 + emphasis1.B) / 3.0)); + Color antializeColor = Color.FromArgb(r, g, b); + + for (int y = 1; y < Height - 1; y++) + { + for (int x = 1; x < Width - 1; x++) + { + if (GetPixel(x, y) == pattern) + { + if (GetPixel(x - 1, y) == emphasis1 && GetPixel(x, y - 1) == emphasis1) + SetPixel(x, y, antializeColor); + else if (GetPixel(x - 1, y) == emphasis1 && GetPixel(x, y + 1) == emphasis1) + SetPixel(x, y, antializeColor); + else if (GetPixel(x + 1, y) == emphasis1 && GetPixel(x, y + 1) == emphasis1) + SetPixel(x, y, antializeColor); + else if (GetPixel(x + 1, y) == emphasis1 && GetPixel(x, y - 1) == emphasis1) + SetPixel(x, y, antializeColor); + } + } + } + return antializeColor; + } + + public RunLengthTwoParts RunLengthEncodeForDvd(Color background, Color pattern, Color emphasis1, Color emphasis2) + { + var backgroundBuffer = new byte[4]; + backgroundBuffer[0] = background.B; + backgroundBuffer[1] = background.G; + backgroundBuffer[2] = background.R; + backgroundBuffer[3] = background.A; + + var patternBuffer = new byte[4]; + patternBuffer[0] = pattern.B; + patternBuffer[1] = pattern.G; + patternBuffer[2] = pattern.R; + patternBuffer[3] = pattern.A; + + var emphasis1Buffer = new byte[4]; + emphasis1Buffer[0] = emphasis1.B; + emphasis1Buffer[1] = emphasis1.G; + emphasis1Buffer[2] = emphasis1.R; + emphasis1Buffer[3] = emphasis1.A; + + var emphasis2Buffer = new byte[4]; + emphasis2Buffer[0] = emphasis2.B; + emphasis2Buffer[1] = emphasis2.G; + emphasis2Buffer[2] = emphasis2.R; + emphasis2Buffer[3] = emphasis2.A; + + var bufferEqual = new byte[Width * Height]; + var bufferUnEqual = new byte[Width * Height]; + int indexBufferEqual = 0; + int indexBufferUnEqual = 0; + + _pixelAddress = -4; + for (int y = 0; y < Height; y++) + { + int index; + byte[] buffer; + if (y % 2 == 0) + { + index = indexBufferEqual; + buffer = bufferEqual; + } + else + { + index = indexBufferUnEqual; + buffer = bufferUnEqual; + } + var indexHalfNibble = false; + var lastColor = -1; + var count = 0; + + for (int x = 0; x < Width; x++) + { + int color = GetDvdColor(patternBuffer, emphasis1Buffer, emphasis2Buffer); + + if (lastColor == -1) + { + lastColor = color; + count = 1; + } + else if (lastColor == color && count < 64) // only allow up to 63 run-length (for SubtitleCreator compatibility) + { + count++; + } + else + { + WriteRle(ref indexHalfNibble, lastColor, count, ref index, buffer); + lastColor = color; + count = 1; + } + } + if (count > 0) + WriteRle(ref indexHalfNibble, lastColor, count, ref index, buffer); + + if (indexHalfNibble) + index++; + + if (y % 2 == 0) + { + indexBufferEqual = index; + bufferEqual = buffer; + } + else + { + indexBufferUnEqual = index; + bufferUnEqual = buffer; + } + } + + var twoParts = new RunLengthTwoParts { Buffer1 = new byte[indexBufferEqual] }; + Buffer.BlockCopy(bufferEqual, 0, twoParts.Buffer1, 0, indexBufferEqual); + twoParts.Buffer2 = new byte[indexBufferUnEqual + 2]; + Buffer.BlockCopy(bufferUnEqual, 0, twoParts.Buffer2, 0, indexBufferUnEqual); + return twoParts; + } + + private static void WriteRle(ref bool indexHalfNibble, int lastColor, int count, ref int index, byte[] buffer) + { + if (count <= Helper.B00000011) // 1-3 repetitions + { + WriteOneNibble(buffer, count, lastColor, ref index, ref indexHalfNibble); + } + else if (count <= Helper.B00001111) // 4-15 repetitions + { + WriteTwoNibbles(buffer, count, lastColor, ref index, indexHalfNibble); + } + else if (count <= Helper.B00111111) // 4-15 repetitions + { + WriteThreeNibbles(buffer, count, lastColor, ref index, ref indexHalfNibble); // 16-63 repetitions + } + else // 64-255 repetitions + { + int factor = count / 255; + for (int i = 0; i < factor; i++) + WriteFourNibbles(buffer, 0xff, lastColor, ref index, indexHalfNibble); + + int rest = count % 255; + if (rest > 0) + WriteFourNibbles(buffer, rest, lastColor, ref index, indexHalfNibble); + } + } + + private static void WriteFourNibbles(byte[] buffer, int count, int color, ref int index, bool indexHalfNibble) + { + int n = (count << 2) + color; + if (indexHalfNibble) + { + index++; + var firstNibble = (byte)(n >> 4); + buffer[index] = firstNibble; + index++; + var secondNibble = (byte)((n & Helper.B00001111) << 4); + buffer[index] = secondNibble; + } + else + { + var firstNibble = (byte)(n >> 8); + buffer[index] = firstNibble; + index++; + var secondNibble = (byte)(n & Helper.B11111111); + buffer[index] = secondNibble; + index++; + } + } + + private static void WriteThreeNibbles(byte[] buffer, int count, int color, ref int index, ref bool indexHalfNibble) + { + //Value Bits n=length, c=color + //16-63 12 0 0 0 0 n n n n n n c c (one and a half byte) + var n = (ushort)((count << 2) + color); + if (indexHalfNibble) + { + index++; // there should already zeroes in last nibble + buffer[index] = (byte)n; + index++; + } + else + { + buffer[index] = (byte)(n >> 4); + index++; + buffer[index] = (byte)((n & Helper.B00011111) << 4); + } + indexHalfNibble = !indexHalfNibble; + } + + private static void WriteTwoNibbles(byte[] buffer, int count, int color, ref int index, bool indexHalfNibble) + { + //Value Bits n=length, c=color + //4-15 8 0 0 n n n n c c (one byte) + var n = (byte)((count << 2) + color); + if (indexHalfNibble) + { + var firstNibble = (byte)(n >> 4); + buffer[index] = (byte)(buffer[index] | firstNibble); + var secondNibble = (byte)((n & Helper.B00001111) << 4); + index++; + buffer[index] = secondNibble; + } + else + { + buffer[index] = n; + index++; + } + } + + private static void WriteOneNibble(byte[] buffer, int count, int color, ref int index, ref bool indexHalfNibble) + { + var n = (byte)((count << 2) + color); + if (indexHalfNibble) + { + buffer[index] = (byte)(buffer[index] | n); + index++; + } + else + { + buffer[index] = (byte)(n << 4); + } + indexHalfNibble = !indexHalfNibble; + } + + private int GetDvdColor(byte[] pattern, byte[] emphasis1, byte[] emphasis2) + { + _pixelAddress += 4; + int a = _bitmapData[_pixelAddress + 3]; + int r = _bitmapData[_pixelAddress + 2]; + int g = _bitmapData[_pixelAddress + 1]; + int b = _bitmapData[_pixelAddress]; + + if (pattern[0] == b && pattern[1] == g && pattern[2] == r && pattern[3] == a) + return 1; + if (emphasis1[0] == b && emphasis1[1] == g && emphasis1[2] == r && emphasis1[3] == a) + return 2; + if (emphasis2[0] == b && emphasis2[1] == g && emphasis2[2] == r && emphasis2[3] == a) + return 3; + return 0; + } + + public void CropTransparentSidesAndBottom(int maximumCropping, bool bottom) + { + int leftStart = 0; + bool done = false; + int x = 0; + int y; + while (!done && x < Width) + { + y = 0; + while (!done && y < Height) + { + int alpha = GetAlpha(x, y); + if (alpha != 0) + { + done = true; + leftStart = x; + leftStart -= maximumCropping; + if (leftStart < 0) + leftStart = 0; + } + y++; + } + x++; + } + + int rightEnd = Width - 1; + done = false; + x = Width - 1; + while (!done && x >= 0) + { + y = 0; + while (!done && y < Height) + { + int alpha = GetAlpha(x, y); + if (alpha != 0) + { + done = true; + rightEnd = x; + rightEnd += maximumCropping; + if (rightEnd >= Width) + rightEnd = Width - 1; + } + y++; + } + x--; + } + + //crop bottom + done = false; + int newHeight = Height; + if (bottom) + { + y = Height - 1; + while (!done && y > 0) + { + x = 0; + while (!done && x < Width) + { + int alpha = GetAlpha(x, y); + if (alpha != 0) + { + done = true; + newHeight = y + maximumCropping + 1; + if (newHeight > Height) + newHeight = Height; + } + x++; + } + y--; + } + } + + if (leftStart < 2 && rightEnd >= Width - 3) + return; + + int newWidth = rightEnd - leftStart + 1; + if (newWidth <= 0) + return; + + var newBitmapData = new byte[newWidth * newHeight * 4]; + int index = 0; + for (y = 0; y < newHeight; y++) + { + int pixelAddress = (leftStart * 4) + (y * 4 * Width); + Buffer.BlockCopy(_bitmapData, pixelAddress, newBitmapData, index, 4 * newWidth); + index += 4 * newWidth; + } + Width = newWidth; + Height = newHeight; + _bitmapData = newBitmapData; + } + + public void CropSidesAndBottom(int maximumCropping, Color transparentColor, bool bottom) + { + int leftStart = 0; + bool done = false; + int x = 0; + int y; + while (!done && x < Width) + { + y = 0; + while (!done && y < Height) + { + Color c = GetPixel(x, y); + if (c != transparentColor) + { + done = true; + leftStart = x; + leftStart -= maximumCropping; + if (leftStart < 0) + leftStart = 0; + } + y++; + } + x++; + } + + int rightEnd = Width - 1; + done = false; + x = Width - 1; + while (!done && x >= 0) + { + y = 0; + while (!done && y < Height) + { + Color c = GetPixel(x, y); + if (c != transparentColor) + { + done = true; + rightEnd = x; + rightEnd += maximumCropping; + if (rightEnd >= Width) + rightEnd = Width - 1; + } + y++; + } + x--; + } + + //crop bottom + done = false; + int newHeight = Height; + if (bottom) + { + y = Height - 1; + while (!done && y > 0) + { + x = 0; + while (!done && x < Width) + { + Color c = GetPixel(x, y); + if (c != transparentColor) + { + done = true; + newHeight = y + maximumCropping; + if (newHeight > Height) + newHeight = Height; + } + x++; + } + y--; + } + } + + if (leftStart < 2 && rightEnd >= Width - 3) + return; + + int newWidth = rightEnd - leftStart + 1; + if (newWidth <= 0) + return; + + var newBitmapData = new byte[newWidth * newHeight * 4]; + int index = 0; + for (y = 0; y < newHeight; y++) + { + int pixelAddress = (leftStart * 4) + (y * 4 * Width); + Buffer.BlockCopy(_bitmapData, pixelAddress, newBitmapData, index, 4 * newWidth); + index += 4 * newWidth; + } + Width = newWidth; + Height = newHeight; + _bitmapData = newBitmapData; + } + + public void CropTop(int maximumCropping, Color transparentColor) + { + bool done = false; + int newTop = 0; + int y = 0; + while (!done && y < Height) + { + var x = 0; + while (!done && x < Width) + { + Color c = GetPixel(x, y); + if (c != transparentColor && !(c.A == 0 && transparentColor.A == 0)) + { + done = true; + newTop = y - maximumCropping; + if (newTop < 0) + newTop = 0; + } + x++; + } + y++; + } + + if (newTop == 0) + return; + + int newHeight = Height - newTop; + var newBitmapData = new byte[Width * newHeight * 4]; + int index = 0; + for (y = newTop; y < Height; y++) + { + int pixelAddress = y * 4 * Width; + Buffer.BlockCopy(_bitmapData, pixelAddress, newBitmapData, index, 4 * Width); + index += 4 * Width; + } + Height = newHeight; + _bitmapData = newBitmapData; + } + + public int CropTopTransparent(int maximumCropping) + { + bool done = false; + int newTop = 0; + int y = 0; + while (!done && y < Height) + { + var x = 0; + while (!done && x < Width) + { + int alpha = GetAlpha(x, y); + if (alpha > 10) + { + done = true; + newTop = y - maximumCropping; + if (newTop < 0) + newTop = 0; + } + x++; + } + y++; + } + + if (newTop == 0) + return 0; + + int newHeight = Height - newTop; + var newBitmapData = new byte[Width * newHeight * 4]; + int index = 0; + for (y = newTop; y < Height; y++) + { + int pixelAddress = y * 4 * Width; + Buffer.BlockCopy(_bitmapData, pixelAddress, newBitmapData, index, 4 * Width); + index += 4 * Width; + } + Height = newHeight; + _bitmapData = newBitmapData; + return newTop; + } + + public void Fill(Color color) + { + var buffer = new byte[4]; + buffer[0] = color.B; + buffer[1] = color.G; + buffer[2] = color.R; + buffer[3] = color.A; + for (int i = 0; i < _bitmapData.Length; i += 4) + Buffer.BlockCopy(buffer, 0, _bitmapData, i, 4); + } + + public int GetAlpha(int x, int y) + { + return _bitmapData[(x * 4) + (y * 4 * Width) + 3]; + } + + public Color GetPixel(int x, int y) + { + _pixelAddress = (x * 4) + (y * 4 * Width); + return Color.FromArgb(_bitmapData[_pixelAddress + 3], _bitmapData[_pixelAddress + 2], _bitmapData[_pixelAddress + 1], _bitmapData[_pixelAddress]); + } + + public byte[] GetPixelColors(int x, int y) + { + _pixelAddress = (x * 4) + (y * 4 * Width); + return new[] { _bitmapData[_pixelAddress + 3], _bitmapData[_pixelAddress + 2], _bitmapData[_pixelAddress + 1], _bitmapData[_pixelAddress] }; + } + + public Color GetPixelNext() + { + _pixelAddress += 4; + return Color.FromArgb(_bitmapData[_pixelAddress + 3], _bitmapData[_pixelAddress + 2], _bitmapData[_pixelAddress + 1], _bitmapData[_pixelAddress]); + } + + public void SetPixel(int x, int y, Color color) + { + _pixelAddress = (x * 4) + (y * 4 * Width); + _bitmapData[_pixelAddress] = color.B; + _bitmapData[_pixelAddress + 1] = color.G; + _bitmapData[_pixelAddress + 2] = color.R; + _bitmapData[_pixelAddress + 3] = color.A; + } + + public void SetPixelNext(Color color) + { + _pixelAddress += 4; + _bitmapData[_pixelAddress] = color.B; + _bitmapData[_pixelAddress + 1] = color.G; + _bitmapData[_pixelAddress + 2] = color.R; + _bitmapData[_pixelAddress + 3] = color.A; + } + + public Bitmap GetBitmap() + { + var bitmap = new Bitmap(Width, Height, PixelFormat.Format32bppArgb); + BitmapData bitmapdata = bitmap.LockBits(new Rectangle(0, 0, Width, Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); + IntPtr destination = bitmapdata.Scan0; + Marshal.Copy(_bitmapData, 0, destination, _bitmapData.Length); + bitmap.UnlockBits(bitmapdata); + return bitmap; + } + + private static int FindBestMatch(Color color, List palette, out int maxDiff) + { + int smallestDiff = 1000; + int smallestDiffIndex = -1; + int i = 0; + foreach (var pc in palette) + { + int diff = Math.Abs(pc.A - color.A) + Math.Abs(pc.R - color.R) + Math.Abs(pc.G - color.G) + Math.Abs(pc.B - color.B); + if (diff < smallestDiff) + { + smallestDiff = diff; + smallestDiffIndex = i; + if (smallestDiff < 4) + { + maxDiff = smallestDiff; + return smallestDiffIndex; + } + } + i++; + } + maxDiff = smallestDiff; + return smallestDiffIndex; + } + + public Bitmap ConverTo8BitsPerPixel() + { + var newBitmap = new Bitmap(Width, Height, PixelFormat.Format8bppIndexed); + var palette = new List { Color.Transparent }; + ColorPalette bPalette = newBitmap.Palette; + var entries = bPalette.Entries; + for (int i = 0; i < newBitmap.Palette.Entries.Length; i++) + entries[i] = Color.Transparent; + + BitmapData data = newBitmap.LockBits(new Rectangle(0, 0, Width, Height), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed); + var bytes = new byte[data.Height * data.Stride]; + Marshal.Copy(data.Scan0, bytes, 0, bytes.Length); + + for (int y = 0; y < Height; y++) + { + for (int x = 0; x < Width; x++) + { + Color c = GetPixel(x, y); + if (c.A < 5) + { + bytes[y * data.Stride + x] = 0; + } + else + { + int maxDiff; + int index = FindBestMatch(c, palette, out maxDiff); + + if (index == -1 && palette.Count < 255) + { + index = palette.Count; + entries[index] = c; + palette.Add(c); + bytes[y * data.Stride + x] = (byte)index; + } + else if (palette.Count < 200 && maxDiff > 5) + { + index = palette.Count; + entries[index] = c; + palette.Add(c); + bytes[y * data.Stride + x] = (byte)index; + } + else if (palette.Count < 255 && maxDiff > 15) + { + index = palette.Count; + entries[index] = c; + palette.Add(c); + bytes[y * data.Stride + x] = (byte)index; + } + else if (index >= 0) + { + bytes[y * data.Stride + x] = (byte)index; + } + } + } + } + Marshal.Copy(bytes, 0, data.Scan0, bytes.Length); + newBitmap.UnlockBits(data); + newBitmap.Palette = bPalette; + return newBitmap; + } + + public NikseBitmap CopyRectangle(Rectangle section) + { + if (section.Bottom > Height) + section = new Rectangle(section.Left, section.Top, section.Width, Height - section.Top); + if (section.Width + section.Left > Width) + section = new Rectangle(section.Left, section.Top, Width - section.Left, section.Height); + var newBitmapData = new byte[section.Width * section.Height * 4]; + int index = 0; + for (int y = section.Top; y < section.Bottom; y++) + { + int pixelAddress = (section.Left * 4) + (y * 4 * Width); + Buffer.BlockCopy(_bitmapData, pixelAddress, newBitmapData, index, 4 * section.Width); + index += 4 * section.Width; + } + return new NikseBitmap(section.Width, section.Height, newBitmapData); + } + + /// + /// Returns brightest color (not white though) + /// + /// Brightest color, if not found or if brightes color is white, then Color.Transparent is returned + public Color GetBrightestColor() + { + int max = Width * Height - 4; + Color brightest = Color.Black; + for (int i = 0; i < max; i++) + { + Color c = GetPixelNext(); + if (c.A > 220 && c.R + c.G + c.B > 200 && c.R + c.G + c.B > brightest.R + brightest.G + brightest.B) + brightest = c; + } + if (IsColorClose(Color.White, brightest, 40)) + return Color.Transparent; + if (IsColorClose(Color.Black, brightest, 10)) + return Color.Transparent; + return brightest; + } + + private static bool IsColorClose(Color color1, Color color2, int maxDiff) + { + if (Math.Abs(color1.R - color2.R) < maxDiff && Math.Abs(color1.G - color2.G) < maxDiff && Math.Abs(color1.B - color2.B) < maxDiff) + return true; + return false; + } + + public void GrayScale() + { + for (int i = 0; i < _bitmapData.Length; i += 4) + { + int medium = Convert.ToInt32((_bitmapData[i + 2] + _bitmapData[i + 1] + _bitmapData[i]) * 1.5 / 3.0 + 2); + if (medium > byte.MaxValue) + medium = byte.MaxValue; + _bitmapData[i + 2] = _bitmapData[i + 1] = _bitmapData[i] = (byte)medium; + } + } + + /// + /// Make pixels with some transparency completely transparent + /// + /// Min alpha value, 0=transparent, 255=fully visible + public void MakeBackgroundTransparent(int minAlpha) + { + var buffer = new byte[4]; + buffer[0] = 0; // B + buffer[1] = 0; // G + buffer[2] = 0; // R + buffer[3] = 0; // A + for (int i = 0; i < _bitmapData.Length; i += 4) + { + if (_bitmapData[i + 3] < minAlpha) + Buffer.BlockCopy(buffer, 0, _bitmapData, i, 4); + } + } + + public void MakeTwoColor(int minRgb) + { + var buffer = new byte[4]; + buffer[0] = 0; // B + buffer[1] = 0; // G + buffer[2] = 0; // R + buffer[3] = 0; // A + var bufferWhite = new byte[4]; + bufferWhite[0] = 255; // B + bufferWhite[1] = 255; // G + bufferWhite[2] = 255; // R + bufferWhite[3] = 255; // A + for (int i = 0; i < _bitmapData.Length; i += 4) + { + if (_bitmapData[i + 3] < 1 || (_bitmapData[i + 0] + _bitmapData[i + 1] + _bitmapData[i + 2] < minRgb)) + Buffer.BlockCopy(buffer, 0, _bitmapData, i, 4); + else + Buffer.BlockCopy(bufferWhite, 0, _bitmapData, i, 4); + } + } + + public void MakeVerticalLinePartTransparent(int xStart, int xEnd, int y) + { + if (xEnd > Width - 1) + xEnd = Width - 1; + if (xStart < 0) + xStart = 0; + + int i = (xStart * 4) + (y * 4 * Width); + int end = (xEnd * 4) + (y * 4 * Width) + 4; + while (i < end) + { + _bitmapData[i] = 0; + i++; + } + } + + public void AddTransparentLineRight() + { + int newWidth = Width + 1; + + var newBitmapData = new byte[newWidth * Height * 4]; + int index = 0; + for (int y = 0; y < Height; y++) + { + int pixelAddress = (0 * 4) + (y * 4 * Width); + Buffer.BlockCopy(_bitmapData, pixelAddress, newBitmapData, index, 4 * Width); + index += 4 * newWidth; + } + Width = newWidth; + _bitmapData = newBitmapData; + for (int y = 0; y < Height; y++) + { + SetPixel(Width - 1, y, Color.Transparent); + } + } + + public void SaveAsTarga(string fileName) + { + // TGA header (18-byte fixed header) + byte[] header = + { + 0, // ID length (1 bytes) + 0, // no color map (1 bytes) + 2, // uncompressed, true color (1 bytes) + 0, 0, // Color map First Entry Index + 0, 0, // Color map Length + 0, // Color map Entry Size + 0, 0, 0, 0, // x and y origin + (byte)(Width & 0x00FF), + (byte)((Width & 0xFF00) >> 8), + (byte)(Height & 0x00FF), + (byte)((Height & 0xFF00) >> 8), + 32, // pixel depth - 32=32 bit bitmap + 0 // Image Descriptor + }; + + var pixels = new byte[_bitmapData.Length]; + int offsetDest = 0; + for (int y = Height-1; y >= 0; y--) // takes lines from bottom lines to top (mirrowed horizontally) + { + for (int x = 0; x < Width; x++) + { + var c = GetPixel(x, y); + pixels[offsetDest] = c.B; + pixels[offsetDest + 1] = c.G; + pixels[offsetDest + 2] = c.R; + pixels[offsetDest + 3] = c.A; + offsetDest += 4; + } + } + + using (var fileStream = File.Create(fileName)) + { + fileStream.Write(header, 0, header.Length); + fileStream.Write(pixels, 0, pixels.Length); + } + } + + } +} \ No newline at end of file diff --git a/libse/NoBreakAfterItem.cs b/libse/NoBreakAfterItem.cs new file mode 100644 index 000000000..09e47be55 --- /dev/null +++ b/libse/NoBreakAfterItem.cs @@ -0,0 +1,56 @@ +using System; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core +{ + public class NoBreakAfterItem : IComparable + { + public readonly Regex Regex; + public readonly string Text; + + public NoBreakAfterItem(Regex regex, string text) + { + Regex = regex; + Text = text; + } + + public NoBreakAfterItem(string text) + { + Text = text; + } + + public bool IsMatch(string line) + { + if (Regex != null) + return Regex.IsMatch(line); + + if (!string.IsNullOrEmpty(Text) && line.EndsWith(Text, StringComparison.Ordinal)) + return true; + + return false; + } + + public override string ToString() + { + return Text; + } + + public int CompareTo(object obj) + { + if (obj == null) + return -1; + + var o = obj as NoBreakAfterItem; + if (o == null) + return -1; + + if (o.Text == null && this.Text == null) + return 0; + + if (o.Text == null) + return -1; + + return string.Compare(Text, o.Text, StringComparison.Ordinal); + } + } +} \ No newline at end of file diff --git a/libse/Paragraph.cs b/libse/Paragraph.cs new file mode 100644 index 000000000..9d0c0746a --- /dev/null +++ b/libse/Paragraph.cs @@ -0,0 +1,154 @@ +using System; + +namespace Nikse.SubtitleEdit.Core +{ + public class Paragraph + { + public int Number { get; set; } + + public string Text { get; set; } + + public TimeCode StartTime { get; set; } + + public TimeCode EndTime { get; set; } + + public TimeCode Duration + { + get + { + return new TimeCode(EndTime.TotalMilliseconds - StartTime.TotalMilliseconds); + } + } + + public int StartFrame { get; set; } + + public int EndFrame { get; set; } + + public bool Forced { get; set; } + + public string Extra { get; set; } + + public bool IsComment { get; set; } + + public string Actor { get; set; } + + public string Effect { get; set; } + + public int Layer { get; set; } + + public string ID { get; private set; } + + public string Language { get; set; } + + public string Style { get; set; } + + public bool NewSection { get; set; } + + private string GenerateId() + { + return Guid.NewGuid().ToString(); + } + + public Paragraph() + { + StartTime = TimeCode.FromSeconds(0); + EndTime = TimeCode.FromSeconds(0); + Text = string.Empty; + ID = GenerateId(); + } + + public Paragraph(TimeCode startTime, TimeCode endTime, string text) + { + StartTime = startTime; + EndTime = endTime; + Text = text; + ID = GenerateId(); + } + + public Paragraph(Paragraph paragraph, bool generateNewId = true) + { + Number = paragraph.Number; + Text = paragraph.Text; + StartTime = new TimeCode(paragraph.StartTime.TotalMilliseconds); + EndTime = new TimeCode(paragraph.EndTime.TotalMilliseconds); + StartFrame = paragraph.StartFrame; + EndFrame = paragraph.EndFrame; + Forced = paragraph.Forced; + Extra = paragraph.Extra; + IsComment = paragraph.IsComment; + Actor = paragraph.Actor; + Effect = paragraph.Effect; + Layer = paragraph.Layer; + ID = generateNewId ? GenerateId() : paragraph.ID; + Language = paragraph.Language; + Style = paragraph.Style; + NewSection = paragraph.NewSection; + } + + public Paragraph(int startFrame, int endFrame, string text) + { + StartTime = new TimeCode(0, 0, 0, 0); + EndTime = new TimeCode(0, 0, 0, 0); + StartFrame = startFrame; + EndFrame = endFrame; + Text = text; + ID = GenerateId(); + } + + public Paragraph(string text, double startTotalMilliseconds, double endTotalMilliseconds) + { + StartTime = new TimeCode(startTotalMilliseconds); + EndTime = new TimeCode(endTotalMilliseconds); + Text = text; + ID = GenerateId(); + } + + public void Adjust(double factor, double adjust) + { + if (StartTime.IsMaxTime) + return; + + double seconds = StartTime.TimeSpan.TotalSeconds * factor + adjust; + StartTime.TimeSpan = TimeSpan.FromSeconds(seconds); + + seconds = EndTime.TimeSpan.TotalSeconds * factor + adjust; + EndTime.TimeSpan = TimeSpan.FromSeconds(seconds); + } + + public void CalculateFrameNumbersFromTimeCodes(double frameRate) + { + StartFrame = (int)Math.Round((StartTime.TotalMilliseconds / TimeCode.BaseUnit * frameRate)); + EndFrame = (int)Math.Round((EndTime.TotalMilliseconds / TimeCode.BaseUnit * frameRate)); + } + + public void CalculateTimeCodesFromFrameNumbers(double frameRate) + { + StartTime.TotalMilliseconds = StartFrame * (TimeCode.BaseUnit / frameRate); + EndTime.TotalMilliseconds = EndFrame * (TimeCode.BaseUnit / frameRate); + } + + public override string ToString() + { + return StartTime + " --> " + EndTime + " " + Text; + } + + public int NumberOfLines + { + get + { + return Utilities.GetNumberOfLines(Text); + } + } + + public double WordsPerMinute + { + get + { + if (string.IsNullOrEmpty(Text)) + return 0; + int wordCount = HtmlUtil.RemoveHtmlTags(Text, true).Split(new[] { ' ', ',', '.', '!', '?', ';', ':', '(', ')', '[', ']', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries).Length; + return (60.0 / Duration.TotalSeconds) * wordCount; + } + } + } +} \ No newline at end of file diff --git a/libse/Properties/AssemblyInfo.cs b/libse/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..f6d210782 --- /dev/null +++ b/libse/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("libse")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("libse")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("eab3b9db-0f82-4843-982b-05a4c51888a2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/libse/RegistryUtil.cs b/libse/RegistryUtil.cs new file mode 100644 index 000000000..3ae073d79 --- /dev/null +++ b/libse/RegistryUtil.cs @@ -0,0 +1,43 @@ +using System.Security; +using Microsoft.Win32; + +namespace Nikse.SubtitleEdit.Core +{ + public static class RegistryUtil + { + /// + /// Retrieves the specified registry subkey value. + /// + /// The path of the subkey to open. + /// The name of the value to retrieve. + /// The value of the subkey requested, or null if the operation failed. + public static string GetValue(string keyName, string valueName) + { + RegistryKey key = null; + try + { + key = Registry.LocalMachine.OpenSubKey(keyName); + if (key != null) + { + var value = key.GetValue(valueName); + if (value != null) + { + return (string)value; + } + } + } + catch (SecurityException) + { + // The user does not have the permissions required to read the registry key. + } + finally + { + if (key != null) + { + key.Dispose(); + } + } + return null; + } + } +} \ No newline at end of file diff --git a/libse/Settings.cs b/libse/Settings.cs new file mode 100644 index 000000000..fdaa60e6b --- /dev/null +++ b/libse/Settings.cs @@ -0,0 +1,3216 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Globalization; +using System.IO; +using System.Text; +using System.Xml; +using System.Xml.Serialization; + +namespace Nikse.SubtitleEdit.Core +{ + // The settings classes are built for easy xml-serialization (makes save/load code simple) + // ...but the built-in serialization is too slow - so a custom (de-)serialization has been used! + + public class RecentFileEntry + { + public string FileName { get; set; } + public string OriginalFileName { get; set; } + public string VideoFileName { get; set; } + public int FirstVisibleIndex { get; set; } + public int FirstSelectedIndex { get; set; } + } + + public class RecentFilesSettings + { + private const int MaxRecentFiles = 25; + + [XmlArrayItem("FileName")] + public List Files { get; set; } + + public RecentFilesSettings() + { + Files = new List(); + } + + public void Add(string fileName, int firstVisibleIndex, int firstSelectedIndex, string videoFileName, string originalFileName) + { + var newList = new List { new RecentFileEntry { FileName = fileName, FirstVisibleIndex = firstVisibleIndex, FirstSelectedIndex = firstSelectedIndex, VideoFileName = videoFileName, OriginalFileName = originalFileName } }; + int index = 0; + foreach (var oldRecentFile in Files) + { + if (!fileName.Equals(oldRecentFile.FileName, StringComparison.OrdinalIgnoreCase) && index < MaxRecentFiles) + newList.Add(new RecentFileEntry { FileName = oldRecentFile.FileName, FirstVisibleIndex = oldRecentFile.FirstVisibleIndex, FirstSelectedIndex = oldRecentFile.FirstSelectedIndex, VideoFileName = oldRecentFile.VideoFileName, OriginalFileName = oldRecentFile.OriginalFileName }); + index++; + } + Files = newList; + } + + public void Add(string fileName, string videoFileName, string originalFileName) + { + var newList = new List(); + foreach (var oldRecentFile in Files) + { + if (fileName.Equals(oldRecentFile.FileName, StringComparison.OrdinalIgnoreCase)) + newList.Add(new RecentFileEntry { FileName = oldRecentFile.FileName, FirstVisibleIndex = oldRecentFile.FirstVisibleIndex, FirstSelectedIndex = oldRecentFile.FirstSelectedIndex, VideoFileName = oldRecentFile.VideoFileName, OriginalFileName = oldRecentFile.OriginalFileName }); + } + if (newList.Count == 0) + newList.Add(new RecentFileEntry { FileName = fileName, FirstVisibleIndex = -1, FirstSelectedIndex = -1, VideoFileName = videoFileName, OriginalFileName = originalFileName }); + + int index = 0; + foreach (var oldRecentFile in Files) + { + if (!fileName.Equals(oldRecentFile.FileName, StringComparison.OrdinalIgnoreCase) && index < MaxRecentFiles) + newList.Add(new RecentFileEntry { FileName = oldRecentFile.FileName, FirstVisibleIndex = oldRecentFile.FirstVisibleIndex, FirstSelectedIndex = oldRecentFile.FirstSelectedIndex, VideoFileName = oldRecentFile.VideoFileName, OriginalFileName = oldRecentFile.OriginalFileName }); + index++; + } + Files = newList; + } + + } + + public class ToolsSettings + { + public int StartSceneIndex { get; set; } + public int EndSceneIndex { get; set; } + public int VerifyPlaySeconds { get; set; } + public int MergeLinesShorterThan { get; set; } + public bool FixShortDisplayTimesAllowMoveStartTime { get; set; } + public string MusicSymbol { get; set; } + public string MusicSymbolToReplace { get; set; } + public string UnicodeSymbolsToInsert { get; set; } + public bool SpellCheckAutoChangeNames { get; set; } + public bool SpellCheckOneLetterWords { get; set; } + public bool SpellCheckEnglishAllowInQuoteAsIng { get; set; } + public bool SpellCheckShowCompletedMessage { get; set; } + public bool OcrFixUseHardcodedRules { get; set; } + public string Interjections { get; set; } + public string MicrosoftBingApiId { get; set; } + public string GoogleApiKey { get; set; } + public bool UseGooleApiPaidService { get; set; } + public string GoogleTranslateLastTargetLanguage { get; set; } + public bool ListViewSyntaxColorDurationSmall { get; set; } + public bool ListViewSyntaxColorDurationBig { get; set; } + public bool ListViewSyntaxColorOverlap { get; set; } + public bool ListViewSyntaxColorLongLines { get; set; } + public bool ListViewSyntaxMoreThanXLines { get; set; } + public int ListViewSyntaxMoreThanXLinesX { get; set; } + public Color ListViewSyntaxErrorColor { get; set; } + public Color ListViewUnfocusedSelectedColor { get; set; } + public bool SplitAdvanced { get; set; } + public string SplitOutputFolder { get; set; } + public int SplitNumberOfParts { get; set; } + public string SplitVia { get; set; } + public string LastShowEarlierOrLaterSelection { get; set; } + public string NewEmptyTranslationText { get; set; } + public string BatchConvertOutputFolder { get; set; } + public bool BatchConvertOverwriteExisting { get; set; } + public bool BatchConvertOverwriteOriginal { get; set; } + public bool BatchConvertRemoveFormatting { get; set; } + public bool BatchConvertFixCasing { get; set; } + public bool BatchConvertRemoveTextForHI { get; set; } + public bool BatchConvertFixCommonErrors { get; set; } + public bool BatchConvertMultipleReplace { get; set; } + public bool BatchConvertSplitLongLines { get; set; } + public bool BatchConvertAutoBalance { get; set; } + public bool BatchConvertSetMinDisplayTimeBetweenSubtitles { get; set; } + public string BatchConvertLanguage { get; set; } + public string BatchConvertFormat { get; set; } + public string ModifySelectionText { get; set; } + public string ModifySelectionRule { get; set; } + public bool ModifySelectionCaseSensitive { get; set; } + public string ExportVobSubFontName { get; set; } + public int ExportVobSubFontSize { get; set; } + public string ExportVobSubVideoResolution { get; set; } + public string ExportVobSubLanguage { get; set; } + public bool ExportVobSubSimpleRendering { get; set; } + public bool ExportVobAntiAliasingWithTransparency { get; set; } + public string ExportBluRayFontName { get; set; } + public int ExportBluRayFontSize { get; set; } + public string ExportFcpFontName { get; set; } + public string ExportFontNameOther { get; set; } + public int ExportFcpFontSize { get; set; } + public string ExportFcpImageType { get; set; } + public string ExportBdnXmlImageType { get; set; } + public int ExportLastFontSize { get; set; } + public int ExportLastLineHeight { get; set; } + public int ExportLastBorderWidth { get; set; } + public bool ExportLastFontBold { get; set; } + public string ExportBluRayVideoResolution { get; set; } + public Color ExportFontColor { get; set; } + public Color ExportBorderColor { get; set; } + public Color ExportShadowColor { get; set; } + public int ExportBottomMargin { get; set; } + public int ExportHorizontalAlignment { get; set; } + public int ExportBluRayBottomMargin { get; set; } + public int ExportBluRayShadow { get; set; } + public int Export3DType { get; set; } + public int Export3DDepth { get; set; } + public int ExportLastShadowTransparency { get; set; } + public double ExportLastFrameRate { get; set; } + public string ExportPenLineJoin { get; set; } + public bool FixCommonErrorsFixOverlapAllowEqualEndStart { get; set; } + public string ImportTextSplitting { get; set; } + public bool ImportTextMergeShortLines { get; set; } + public string ImportTextLineBreak { get; set; } + public string GenerateTimeCodePatterns { get; set; } + public string MusicSymbolStyle { get; set; } + public int BridgeGapMilliseconds { get; set; } + public string ExportCustomTemplates { get; set; } + public string ChangeCasingChoice { get; set; } + public bool UseNoLineBreakAfter { get; set; } + public string NoLineBreakAfterEnglish { get; set; } + public List FindHistory { get; set; } + + public ToolsSettings() + { + StartSceneIndex = 1; + EndSceneIndex = 1; + VerifyPlaySeconds = 2; + MergeLinesShorterThan = 33; + FixShortDisplayTimesAllowMoveStartTime = false; + MusicSymbol = "♪"; + MusicSymbolToReplace = "♪ ⶠ♪ âTª ã¢â™âª ?t×3 ?t¤3 #"; + UnicodeSymbolsToInsert = "♪;♫;☺;☹;♥;©;☮;☯;Σ;∞;≡;⇒;π"; + SpellCheckAutoChangeNames = true; + OcrFixUseHardcodedRules = true; + Interjections = "Ah;Ahh;Ahhh;Ahhhh;Eh;Ehh;Ehhh;Hm;Hmm;Hmmm;Huh;Mm;Mmm;Mmmm;Phew;Gah;Oh;Ohh;Ohhh;Ow;Oww;Owww;Ugh;Ughh;Uh;Uhh;Uhhh;Whew"; + MicrosoftBingApiId = "C2C2E9A508E6748F0494D68DFD92FAA1FF9B0BA4"; + GoogleApiKey = "ABQIAAAA4j5cWwa3lDH0RkZceh7PjBTDmNAghl5kWSyuukQ0wtoJG8nFBxRPlalq-gAvbeCXMCkmrysqjXV1Gw"; + UseGooleApiPaidService = false; + GoogleTranslateLastTargetLanguage = "en"; + SpellCheckOneLetterWords = true; + SpellCheckEnglishAllowInQuoteAsIng = false; + SpellCheckShowCompletedMessage = true; + ListViewSyntaxColorDurationSmall = true; + ListViewSyntaxColorDurationBig = true; + ListViewSyntaxColorOverlap = true; + ListViewSyntaxColorLongLines = true; + ListViewSyntaxMoreThanXLines = true; + ListViewSyntaxMoreThanXLinesX = 2; + ListViewSyntaxErrorColor = Color.FromArgb(255, 180, 150); + ListViewUnfocusedSelectedColor = Color.LightBlue; + SplitAdvanced = false; + SplitNumberOfParts = 3; + SplitVia = "Lines"; + NewEmptyTranslationText = string.Empty; + BatchConvertLanguage = "en"; + ModifySelectionRule = "Contains"; + ModifySelectionText = string.Empty; + GenerateTimeCodePatterns = "HH:mm:ss;yyyy-MM-dd;dddd dd MMMM yyyy
HH:mm:ss;dddd dd MMMM yyyy
hh:mm:ss tt;s"; + MusicSymbolStyle = "Double"; // 'Double' or 'Single' + ExportFontColor = Color.White; + ExportBorderColor = Color.Black; + ExportShadowColor = Color.Black; + ExportBottomMargin = 15; + ExportHorizontalAlignment = 1; // 1=center (0=left, 2=right) + ExportVobSubSimpleRendering = true; + ExportVobAntiAliasingWithTransparency = true; + ExportBluRayBottomMargin = 20; + ExportBluRayShadow = 1; + Export3DType = 0; + Export3DDepth = 0; + ExportLastShadowTransparency = 200; + ExportLastFrameRate = 24.0d; + ExportPenLineJoin = "Round"; + ExportFcpImageType = "Bmp"; + ExportLastBorderWidth = 2; + BridgeGapMilliseconds = 100; + ExportCustomTemplates = "SubRipÆÆ{number}\r\n{start} --> {end}\r\n{text}\r\n\r\nÆhh:mm:ss,zzzÆ[Do not modify]ÆæMicroDVDÆÆ{{start}}{{end}}{text}\r\nÆffÆ||Æ"; + UseNoLineBreakAfter = false; + NoLineBreakAfterEnglish = " Mrs.; Ms.; Mr.; Dr.; a; an; the; my; my own; your; his; our; their; it's; is; are;'s; 're; would;'ll;'ve;'d; will; that; which; who; whom; whose; whichever; whoever; wherever; each; either; every; all; both; few; many; sevaral; all; any; most; been; been doing; none; some; my own; your own; his own; her own; our own; their own; I; she; he; as per; as regards; into; onto; than; where as; abaft; aboard; about; above; across; afore; after; against; along; alongside; amid; amidst; among; amongst; anenst; apropos; apud; around; as; aside; astride; at; athwart; atop; barring; before; behind; below; beneath; beside; besides; between; betwixt; beyond; but; by; circa; ca; concerning; despite; down; during; except; excluding; following; for; forenenst; from; given; in; including; inside; into; lest; like; minus; modulo; near; next; of; off; on; onto; opposite; out; outside; over; pace; past; per; plus; pro; qua; regarding; round; sans; save; since; than; through; thru; throughout; thruout; till; to; toward; towards; under; underneath; unlike; until; unto; up; upon; versus; vs; via; vice; with; within; without; considering; respecting; one; two; another; three; our; five; six; seven; eight; nine; ten; eleven; twelve; thirteen; fourteen; fifteen; sixteen; seventeen; eighteen; nineteen; twenty; thirty; forty; fifty; sixty; seventy; eighty; ninety; hundred; thousand; million; billion; trillion; while; however; what; zero; little; enough; after; although; and; as; if; though; although; because; before; both; but; even; how; than; nor; or; only; unless; until; yet; was; were"; + FindHistory = new List(); + ImportTextLineBreak = "|"; + } + + } + + public class WordListSettings + { + public string LastLanguage { get; set; } + public string NamesEtcUrl { get; set; } + public bool UseOnlineNamesEtc { get; set; } + + public WordListSettings() + { + LastLanguage = "en-US"; + NamesEtcUrl = "https://raw.githubusercontent.com/SubtitleEdit/subtitleedit/master/Dictionaries/names_etc.xml"; + } + } + + public class SubtitleSettings + { + public string SsaFontName { get; set; } + public double SsaFontSize { get; set; } + public int SsaFontColorArgb { get; set; } + public int SsaOutline { get; set; } + public int SsaShadow { get; set; } + public bool SsaOpaqueBox { get; set; } + public string DCinemaFontFile { get; set; } + public string DCinemaLoadFontResource { get; set; } + public int DCinemaFontSize { get; set; } + public int DCinemaBottomMargin { get; set; } + public double DCinemaZPosition { get; set; } + public int DCinemaFadeUpTime { get; set; } + public int DCinemaFadeDownTime { get; set; } + + public string CurrentDCinemaSubtitleId { get; set; } + public string CurrentDCinemaMovieTitle { get; set; } + public string CurrentDCinemaReelNumber { get; set; } + public string CurrentDCinemaIssueDate { get; set; } + public string CurrentDCinemaLanguage { get; set; } + public string CurrentDCinemaEditRate { get; set; } + public string CurrentDCinemaTimeCodeRate { get; set; } + public string CurrentDCinemaStartTime { get; set; } + public string CurrentDCinemaFontId { get; set; } + public string CurrentDCinemaFontUri { get; set; } + public Color CurrentDCinemaFontColor { get; set; } + public string CurrentDCinemaFontEffect { get; set; } + public Color CurrentDCinemaFontEffectColor { get; set; } + public int CurrentDCinemaFontSize { get; set; } + + public int CurrentCavena890LanguageIdLine1 { get; set; } + public int CurrentCavena890LanguageIdLine2 { get; set; } + + public bool CheetahCaptionAlwayWriteEndTime { get; set; } + + public bool SamiDisplayTwoClassesAsTwoSubtitles { get; set; } + public int SamiHtmlEncodeMode { get; set; } + + public string TimedText10TimeCodeFormat { get; set; } + public string TimedText10TimeCodeFormatSource { get; set; } + + public int FcpFontSize { get; set; } + public string FcpFontName { get; set; } + + public string NuendoCharacterListFile { get; set; } + + public SubtitleSettings() + { + SsaFontName = "Arial"; + SsaFontSize = 20; + SsaFontColorArgb = Color.FromArgb(255, 255, 255).ToArgb(); + SsaOutline = 2; + SsaShadow = 1; + SsaOpaqueBox = false; + + DCinemaFontFile = "Arial.ttf"; + DCinemaLoadFontResource = "urn:uuid:3dec6dc0-39d0-498d-97d0-928d2eb78391"; + DCinemaFontSize = 42; + DCinemaBottomMargin = 8; + DCinemaZPosition = 0; + DCinemaFadeUpTime = 5; + DCinemaFadeDownTime = 5; + + SamiDisplayTwoClassesAsTwoSubtitles = true; + SamiHtmlEncodeMode = 0; + + TimedText10TimeCodeFormat = "Source"; + + FcpFontSize = 18; + FcpFontName = "Lucida Grande"; + } + + public void InitializeDCinameSettings(bool smpte) + { + if (smpte) + { + CurrentDCinemaSubtitleId = "urn:uuid:" + Guid.NewGuid(); + CurrentDCinemaLanguage = "en"; + CurrentDCinemaFontUri = DCinemaLoadFontResource; + CurrentDCinemaFontId = "theFontId"; + } + else + { + string hex = Guid.NewGuid().ToString().Replace("-", string.Empty).ToLower(); + hex = hex.Insert(8, "-").Insert(13, "-").Insert(18, "-").Insert(23, "-"); + CurrentDCinemaSubtitleId = hex; + CurrentDCinemaLanguage = "English"; + CurrentDCinemaFontUri = DCinemaFontFile; + CurrentDCinemaFontId = "Arial"; + } + CurrentDCinemaIssueDate = DateTime.Now.ToString("s") + ".000-00:00"; + CurrentDCinemaMovieTitle = "title"; + CurrentDCinemaReelNumber = "1"; + CurrentDCinemaFontColor = Color.White; + CurrentDCinemaFontEffect = "border"; + CurrentDCinemaFontEffectColor = Color.Black; + CurrentDCinemaFontSize = DCinemaFontSize; + CurrentCavena890LanguageIdLine1 = -1; + CurrentCavena890LanguageIdLine2 = -1; + } + } + + public class ProxySettings + { + public string ProxyAddress { get; set; } + public string UserName { get; set; } + public string Password { get; set; } + public string Domain { get; set; } + + public string DecodePassword() + { + return Encoding.UTF8.GetString(Convert.FromBase64String(Password)); + } + public void EncodePassword(string unencryptedPassword) + { + Password = Convert.ToBase64String(Encoding.UTF8.GetBytes(unencryptedPassword)); + } + } + + public class FixCommonErrorsSettings + { + public string StartPosition { get; set; } + public string StartSize { get; set; } + public bool EmptyLinesTicked { get; set; } + public bool OverlappingDisplayTimeTicked { get; set; } + public bool TooShortDisplayTimeTicked { get; set; } + public bool TooLongDisplayTimeTicked { get; set; } + public bool InvalidItalicTagsTicked { get; set; } + public bool BreakLongLinesTicked { get; set; } + public bool MergeShortLinesTicked { get; set; } + public bool MergeShortLinesAllTicked { get; set; } + public bool UnneededSpacesTicked { get; set; } + public bool UnneededPeriodsTicked { get; set; } + public bool MissingSpacesTicked { get; set; } + public bool AddMissingQuotesTicked { get; set; } + public bool Fix3PlusLinesTicked { get; set; } + public bool FixHyphensTicked { get; set; } + public bool FixHyphensAddTicked { get; set; } + public bool UppercaseIInsideLowercaseWordTicked { get; set; } + public bool DoubleApostropheToQuoteTicked { get; set; } + public bool AddPeriodAfterParagraphTicked { get; set; } + public bool StartWithUppercaseLetterAfterParagraphTicked { get; set; } + public bool StartWithUppercaseLetterAfterPeriodInsideParagraphTicked { get; set; } + public bool StartWithUppercaseLetterAfterColonTicked { get; set; } + public bool AloneLowercaseIToUppercaseIEnglishTicked { get; set; } + public bool FixOcrErrorsViaReplaceListTicked { get; set; } + public bool RemoveSpaceBetweenNumberTicked { get; set; } + public bool FixDialogsOnOneLineTicked { get; set; } + public bool TurkishAnsiTicked { get; set; } + public bool DanishLetterITicked { get; set; } + public bool SpanishInvertedQuestionAndExclamationMarksTicked { get; set; } + public bool FixDoubleDashTicked { get; set; } + public bool FixDoubleGreaterThanTicked { get; set; } + public bool FixEllipsesStartTicked { get; set; } + public bool FixMissingOpenBracketTicked { get; set; } + public bool FixMusicNotationTicked { get; set; } + + public FixCommonErrorsSettings() + { + EmptyLinesTicked = true; + OverlappingDisplayTimeTicked = true; + TooShortDisplayTimeTicked = true; + TooLongDisplayTimeTicked = true; + InvalidItalicTagsTicked = true; + BreakLongLinesTicked = true; + MergeShortLinesTicked = true; + UnneededPeriodsTicked = true; + UnneededSpacesTicked = true; + MissingSpacesTicked = true; + UppercaseIInsideLowercaseWordTicked = true; + DoubleApostropheToQuoteTicked = true; + AddPeriodAfterParagraphTicked = false; + StartWithUppercaseLetterAfterParagraphTicked = true; + StartWithUppercaseLetterAfterPeriodInsideParagraphTicked = false; + StartWithUppercaseLetterAfterColonTicked = false; + AloneLowercaseIToUppercaseIEnglishTicked = false; + TurkishAnsiTicked = false; + DanishLetterITicked = false; + FixDoubleDashTicked = true; + FixDoubleGreaterThanTicked = true; + FixEllipsesStartTicked = true; + FixMissingOpenBracketTicked = true; + FixMusicNotationTicked = true; + } + } + + public class GeneralSettings + { + public bool ShowToolbarNew { get; set; } + public bool ShowToolbarOpen { get; set; } + public bool ShowToolbarSave { get; set; } + public bool ShowToolbarSaveAs { get; set; } + public bool ShowToolbarFind { get; set; } + public bool ShowToolbarReplace { get; set; } + public bool ShowToolbarFixCommonErrors { get; set; } + public bool ShowToolbarVisualSync { get; set; } + public bool ShowToolbarSpellCheck { get; set; } + public bool ShowToolbarSettings { get; set; } + public bool ShowToolbarHelp { get; set; } + + public bool ShowVideoPlayer { get; set; } + public bool ShowAudioVisualizer { get; set; } + public bool ShowWaveform { get; set; } + public bool ShowSpectrogram { get; set; } + public bool ShowFrameRate { get; set; } + public double DefaultFrameRate { get; set; } + public double CurrentFrameRate { get; set; } + public string DefaultSubtitleFormat { get; set; } + public string DefaultEncoding { get; set; } + public bool AutoConvertToUtf8 { get; set; } + public bool AutoGuessAnsiEncoding { get; set; } + public string SubtitleFontName { get; set; } + public int SubtitleFontSize { get; set; } + public bool SubtitleFontBold { get; set; } + public Color SubtitleFontColor { get; set; } + public Color SubtitleBackgroundColor { get; set; } + public bool CenterSubtitleInTextBox { get; set; } + public bool ShowRecentFiles { get; set; } + public bool RememberSelectedLine { get; set; } + public bool StartLoadLastFile { get; set; } + public bool StartRememberPositionAndSize { get; set; } + public string StartPosition { get; set; } + public string StartSize { get; set; } + public int SplitContainerMainSplitterDistance { get; set; } + public int SplitContainer1SplitterDistance { get; set; } + public int SplitContainerListViewAndTextSplitterDistance { get; set; } + public bool StartInSourceView { get; set; } + public bool RemoveBlankLinesWhenOpening { get; set; } + public int SubtitleLineMaximumLength { get; set; } + public int SubtitleMinimumDisplayMilliseconds { get; set; } + public int SubtitleMaximumDisplayMilliseconds { get; set; } + public int MinimumMillisecondsBetweenLines { get; set; } + public int SetStartEndHumanDelay { get; set; } + public bool AutoWrapLineWhileTyping { get; set; } + public double SubtitleMaximumCharactersPerSeconds { get; set; } + public double SubtitleOptimalCharactersPerSeconds { get; set; } + public string SpellCheckLanguage { get; set; } + public string VideoPlayer { get; set; } + public int VideoPlayerDefaultVolume { get; set; } + public int VideoPlayerPreviewFontSize { get; set; } + public bool VideoPlayerPreviewFontBold { get; set; } + public bool VideoPlayerShowStopButton { get; set; } + public bool VideoPlayerShowFullscreenButton { get; set; } + public bool VideoPlayerShowMuteButton { get; set; } + public string Language { get; set; } + public string ListViewLineSeparatorString { get; set; } + public int ListViewDoubleClickAction { get; set; } + public string UppercaseLetters { get; set; } + public int DefaultAdjustMilliseconds { get; set; } + public bool AutoRepeatOn { get; set; } + public int AutoRepeatCount { get; set; } + public bool AutoContinueOn { get; set; } + public bool SyncListViewWithVideoWhilePlaying { get; set; } + public int AutoBackupSeconds { get; set; } + public string SpellChecker { get; set; } + public bool AllowEditOfOriginalSubtitle { get; set; } + public bool PromptDeleteLines { get; set; } + public bool Undocked { get; set; } + public string UndockedVideoPosition { get; set; } + public string UndockedWaveformPosition { get; set; } + public string UndockedVideoControlsPosition { get; set; } + public bool WaveformCenter { get; set; } + public int SmallDelayMilliseconds { get; set; } + public int LargeDelayMilliseconds { get; set; } + public bool ShowOriginalAsPreviewIfAvailable { get; set; } + public int LastPacCodePage { get; set; } + public string OpenSubtitleExtraExtensions { get; set; } + public bool ListViewColumnsRememberSize { get; set; } + public int ListViewNumberWidth { get; set; } + public int ListViewStartWidth { get; set; } + public int ListViewEndWidth { get; set; } + public int ListViewDurationWidth { get; set; } + public int ListViewTextWidth { get; set; } + public string VlcWaveTranscodeSettings { get; set; } + public string VlcLocation { get; set; } + public string VlcLocationRelative { get; set; } + public string MpcHcLocation { get; set; } + public bool UseFFmpegForWaveExtraction { get; set; } + public string FFmpegLocation { get; set; } + public bool UseTimeFormatHHMMSSFF { get; set; } + public int ClearStatusBarAfterSeconds { get; set; } + public string Company { get; set; } + public bool MoveVideo100Or500MsPlaySmallSample { get; set; } + public bool DisableVideoAutoLoading { get; set; } + public int NewEmptyDefaultMs { get; set; } + public bool RightToLeftMode { get; set; } + public string LastSaveAsFormat { get; set; } + public bool CheckForUpdates { get; set; } + public DateTime LastCheckForUpdates { get; set; } + public bool ShowProgress { get; set; } + public bool ShowBetaStuff { get; set; } + + public GeneralSettings() + { + ShowToolbarNew = true; + ShowToolbarOpen = true; + ShowToolbarSave = true; + ShowToolbarSaveAs = false; + ShowToolbarFind = true; + ShowToolbarReplace = true; + ShowToolbarFixCommonErrors = false; + ShowToolbarVisualSync = true; + ShowToolbarSpellCheck = true; + ShowToolbarSettings = false; + ShowToolbarHelp = true; + + ShowVideoPlayer = false; + ShowAudioVisualizer = false; + ShowWaveform = true; + ShowSpectrogram = true; + ShowFrameRate = false; + DefaultFrameRate = 23.976; + CurrentFrameRate = DefaultFrameRate; + SubtitleFontName = "Tahoma"; + if (Environment.OSVersion.Version.Major < 6) // 6 == Vista/Win2008Server/Win7 + SubtitleFontName = "Times New Roman"; + + SubtitleFontSize = 8; + SubtitleFontBold = false; + SubtitleFontColor = Color.Black; + SubtitleBackgroundColor = Color.White; + CenterSubtitleInTextBox = false; + DefaultSubtitleFormat = "SubRip"; + DefaultEncoding = Encoding.UTF8.EncodingName; + AutoConvertToUtf8 = false; + AutoGuessAnsiEncoding = false; + ShowRecentFiles = true; + RememberSelectedLine = true; + StartLoadLastFile = true; + StartRememberPositionAndSize = true; + SubtitleLineMaximumLength = 43; + SubtitleMinimumDisplayMilliseconds = 1000; + SubtitleMaximumDisplayMilliseconds = 8 * 1000; + MinimumMillisecondsBetweenLines = 24; + SetStartEndHumanDelay = 100; + AutoWrapLineWhileTyping = false; + SubtitleMaximumCharactersPerSeconds = 25.0; + SubtitleOptimalCharactersPerSeconds = 15.0; + SpellCheckLanguage = null; + VideoPlayer = string.Empty; + VideoPlayerDefaultVolume = 75; + VideoPlayerPreviewFontSize = 10; + VideoPlayerPreviewFontBold = true; + VideoPlayerShowStopButton = true; + VideoPlayerShowMuteButton = true; + VideoPlayerShowFullscreenButton = true; + ListViewLineSeparatorString = "
"; + ListViewDoubleClickAction = 1; + UppercaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWZYXÆØÃÅÄÖÉÈÁÂÀÇÊÍÓÔÕÚŁАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯĞİŞÜÙÁÌÑÎ"; + DefaultAdjustMilliseconds = 1000; + AutoRepeatOn = true; + AutoRepeatCount = 2; + AutoContinueOn = false; + SyncListViewWithVideoWhilePlaying = false; + AutoBackupSeconds = 60 * 15; + SpellChecker = "hunspell"; + AllowEditOfOriginalSubtitle = true; + PromptDeleteLines = true; + Undocked = false; + UndockedVideoPosition = "-32000;-32000"; + UndockedWaveformPosition = "-32000;-32000"; + UndockedVideoControlsPosition = "-32000;-32000"; + SmallDelayMilliseconds = 500; + LargeDelayMilliseconds = 5000; + OpenSubtitleExtraExtensions = "*.mp4;*.m4v;*.mkv;*.ts"; // matroska/mp4/m4v files (can contain subtitles) + ListViewColumnsRememberSize = true; + VlcWaveTranscodeSettings = "acodec=s16l"; // "acodec=s16l,channels=1,ab=64,samplerate=8000"; + UseTimeFormatHHMMSSFF = false; + ClearStatusBarAfterSeconds = 10; + MoveVideo100Or500MsPlaySmallSample = false; + DisableVideoAutoLoading = false; + RightToLeftMode = false; + LastSaveAsFormat = string.Empty; + CheckForUpdates = true; + LastCheckForUpdates = DateTime.Now; + ShowProgress = false; + ShowBetaStuff = false; + NewEmptyDefaultMs = 2000; + } + + } + + public class VideoControlsSettings + { + public string CustomSearchText1 { get; set; } + public string CustomSearchText2 { get; set; } + public string CustomSearchText3 { get; set; } + public string CustomSearchText4 { get; set; } + public string CustomSearchText5 { get; set; } + public string CustomSearchText6 { get; set; } + public string CustomSearchUrl1 { get; set; } + public string CustomSearchUrl2 { get; set; } + public string CustomSearchUrl3 { get; set; } + public string CustomSearchUrl4 { get; set; } + public string CustomSearchUrl5 { get; set; } + public string CustomSearchUrl6 { get; set; } + public string LastActiveTab { get; set; } + public bool WaveformDrawGrid { get; set; } + public bool WaveformAllowOverlap { get; set; } + public bool WaveformFocusOnMouseEnter { get; set; } + public bool WaveformListViewFocusOnMouseEnter { get; set; } + public int WaveformBorderHitMs { get; set; } + public Color WaveformGridColor { get; set; } + public Color WaveformColor { get; set; } + public Color WaveformSelectedColor { get; set; } + public Color WaveformBackgroundColor { get; set; } + public Color WaveformTextColor { get; set; } + public int WaveformTextSize { get; set; } + public bool WaveformTextBold { get; set; } + public string WaveformDoubleClickOnNonParagraphAction { get; set; } + public string WaveformRightClickOnNonParagraphAction { get; set; } + public bool WaveformMouseWheelScrollUpIsForward { get; set; } + public bool GenerateSpectrogram { get; set; } + public string SpectrogramAppearance { get; set; } + public int WaveformMinimumSampleRate { get; set; } + public double WaveformSeeksSilenceDurationSeconds { get; set; } + public int WaveformSeeksSilenceMaxVolume { get; set; } + + public VideoControlsSettings() + { + CustomSearchText1 = "The Free Dictionary"; + CustomSearchUrl1 = "http://www.thefreedictionary.com/{0}"; + CustomSearchText2 = "Wikipedia"; + CustomSearchUrl2 = "http://en.m.wikipedia.org/wiki?search={0}"; + + LastActiveTab = "Translate"; + WaveformDrawGrid = true; + WaveformAllowOverlap = false; + WaveformBorderHitMs = 15; + WaveformGridColor = Color.FromArgb(255, 20, 20, 18); + WaveformColor = Color.GreenYellow; + WaveformSelectedColor = Color.Red; + WaveformBackgroundColor = Color.Black; + WaveformTextColor = Color.Gray; + WaveformTextSize = 9; + WaveformTextBold = true; + WaveformDoubleClickOnNonParagraphAction = "PlayPause"; + WaveformDoubleClickOnNonParagraphAction = string.Empty; + WaveformMouseWheelScrollUpIsForward = true; + SpectrogramAppearance = "OneColorGradient"; + WaveformMinimumSampleRate = 126; + WaveformSeeksSilenceDurationSeconds = 0.3; + WaveformSeeksSilenceMaxVolume = 10; + } + } + + public class VobSubOcrSettings + { + public int XOrMorePixelsMakesSpace { get; set; } + public double AllowDifferenceInPercent { get; set; } + public double BlurayAllowDifferenceInPercent { get; set; } + public string LastImageCompareFolder { get; set; } + public int LastModiLanguageId { get; set; } + public string LastOcrMethod { get; set; } + public string TesseractLastLanguage { get; set; } + public bool UseModiInTesseractForUnknownWords { get; set; } + public bool UseItalicsInTesseract { get; set; } + public bool UseMusicSymbolsInTesseract { get; set; } + public bool RightToLeft { get; set; } + public bool TopToBottom { get; set; } + public int DefaultMillisecondsForUnknownDurations { get; set; } + public bool PromptForUnknownWords { get; set; } + public bool GuessUnknownWords { get; set; } + public bool AutoBreakSubtitleIfMoreThanTwoLines { get; set; } + public double ItalicFactor { get; set; } + public bool LineOcrDraw { get; set; } + public bool LineOcrAdvancedItalic { get; set; } + public string LineOcrLastLanguages { get; set; } + public string LineOcrLastSpellCheck { get; set; } + public int LineOcrXOrMorePixelsMakesSpace { get; set; } + public int LineOcrMinLineHeight { get; set; } + public int LineOcrMaxLineHeight { get; set; } + + public VobSubOcrSettings() + { + XOrMorePixelsMakesSpace = 8; + AllowDifferenceInPercent = 1.0; + BlurayAllowDifferenceInPercent = 7.5; + LastImageCompareFolder = "English"; + LastModiLanguageId = 9; + LastOcrMethod = "Tesseract"; + UseItalicsInTesseract = true; + UseMusicSymbolsInTesseract = true; + RightToLeft = false; + TopToBottom = true; + DefaultMillisecondsForUnknownDurations = 5000; + PromptForUnknownWords = true; + GuessUnknownWords = true; + AutoBreakSubtitleIfMoreThanTwoLines = true; + ItalicFactor = 0.2; + } + } + + public class MultipleSearchAndReplaceSetting + { + public bool Enabled { get; set; } + public string FindWhat { get; set; } + public string ReplaceWith { get; set; } + public string SearchType { get; set; } + } + + public class NetworkSettings + { + public string UserName { get; set; } + public string WebServiceUrl { get; set; } + public string SessionKey { get; set; } + public int PollIntervalSeconds { get; set; } + public string NewMessageSound { get; set; } + + public NetworkSettings() + { + UserName = string.Empty; + SessionKey = "DemoSession"; // TODO: Leave blank or use guid + WebServiceUrl = "http://www.nikse.dk/se/SeService.asmx"; + PollIntervalSeconds = 5; + } + } + + public class Shortcuts + { + public string GeneralGoToFirstSelectedLine { get; set; } + public string GeneralGoToNextEmptyLine { get; set; } + public string GeneralMergeSelectedLines { get; set; } + public string GeneralMergeSelectedLinesOnlyFirstText { get; set; } + public string GeneralToggleTranslationMode { get; set; } + public string GeneralSwitchOriginalAndTranslation { get; set; } + public string GeneralMergeOriginalAndTranslation { get; set; } + public string GeneralGoToNextSubtitle { get; set; } + public string GeneralGoToPrevSubtitle { get; set; } + public string GeneralGoToStartOfCurrentSubtitle { get; set; } + public string GeneralGoToEndOfCurrentSubtitle { get; set; } + public string GeneralPlayFirstSelected { get; set; } + public string MainFileNew { get; set; } + public string MainFileOpen { get; set; } + public string MainFileOpenKeepVideo { get; set; } + public string MainFileSave { get; set; } + public string MainFileSaveOriginal { get; set; } + public string MainFileSaveOriginalAs { get; set; } + public string MainFileSaveAs { get; set; } + public string MainFileSaveAll { get; set; } + public string MainFileExportEbu { get; set; } + public string MainEditUndo { get; set; } + public string MainEditRedo { get; set; } + public string MainEditFind { get; set; } + public string MainEditFindNext { get; set; } + public string MainEditReplace { get; set; } + public string MainEditMultipleReplace { get; set; } + public string MainEditGoToLineNumber { get; set; } + public string MainEditRightToLeft { get; set; } + public string MainEditReverseStartAndEndingForRTL { get; set; } + public string MainEditToggleTranslationOriginalInPreviews { get; set; } + public string MainEditInverseSelection { get; set; } + public string MainEditModifySelection { get; set; } + public string MainToolsFixCommonErrors { get; set; } + public string MainToolsFixCommonErrorsPreview { get; set; } + public string MainToolsMergeShortLines { get; set; } + public string MainToolsSplitLongLines { get; set; } + public string MainToolsRenumber { get; set; } + public string MainToolsRemoveTextForHI { get; set; } + public string MainToolsChangeCasing { get; set; } + public string MainToolsAutoDuration { get; set; } + public string MainToolsBatchConvert { get; set; } + public string MainToolsBeamer { get; set; } + public string MainVideoPause { get; set; } + public string MainVideoPlayPauseToggle { get; set; } + public string MainVideoShowHideVideo { get; set; } + public string MainVideoToggleVideoControls { get; set; } + public string MainVideo1FrameLeft { get; set; } + public string MainVideo1FrameRight { get; set; } + public string MainVideo100MsLeft { get; set; } + public string MainVideo100MsRight { get; set; } + public string MainVideo500MsLeft { get; set; } + public string MainVideo500MsRight { get; set; } + public string MainVideo1000MsLeft { get; set; } + public string MainVideo1000MsRight { get; set; } + public string MainVideoFullscreen { get; set; } + public string MainSpellCheck { get; set; } + public string MainSpellCheckFindDoubleWords { get; set; } + public string MainSpellCheckAddWordToNames { get; set; } + public string MainSynchronizationAdjustTimes { get; set; } + public string MainSynchronizationVisualSync { get; set; } + public string MainSynchronizationPointSync { get; set; } + public string MainSynchronizationChangeFrameRate { get; set; } + public string MainListViewItalic { get; set; } + public string MainListViewToggleDashes { get; set; } + public string MainListViewAlignment { get; set; } + public string MainListViewCopyText { get; set; } + public string MainListViewCopyTextFromOriginalToCurrent { get; set; } + public string MainListViewAutoDuration { get; set; } + public string MainListViewColumnDeleteText { get; set; } + public string MainListViewColumnInsertText { get; set; } + public string MainListViewColumnPaste { get; set; } + public string MainListViewFocusWaveform { get; set; } + public string MainListViewGoToNextError { get; set; } + public string MainTextBoxItalic { get; set; } + public string MainTextBoxSplitAtCursor { get; set; } + public string MainTextBoxMoveLastWordDown { get; set; } + public string MainTextBoxMoveFirstWordFromNextUp { get; set; } + public string MainTextBoxSelectionToLower { get; set; } + public string MainTextBoxSelectionToUpper { get; set; } + public string MainTextBoxToggleAutoDuration { get; set; } + public string MainCreateInsertSubAtVideoPos { get; set; } + public string MainCreatePlayFromJustBefore { get; set; } + public string MainCreateSetStart { get; set; } + public string MainCreateSetEnd { get; set; } + public string MainCreateSetEndAddNewAndGoToNew { get; set; } + public string MainCreateStartDownEndUp { get; set; } + public string MainAdjustSetStartAndOffsetTheRest { get; set; } + public string MainAdjustSetEndAndOffsetTheRest { get; set; } + public string MainAdjustSetEndAndOffsetTheRestAndGoToNext { get; set; } + public string MainAdjustSetEndAndGotoNext { get; set; } + public string MainAdjustViaEndAutoStartAndGoToNext { get; set; } + public string MainAdjustSetStartAutoDurationAndGoToNext { get; set; } + public string MainAdjustSetEndNextStartAndGoToNext { get; set; } + public string MainAdjustStartDownEndUpAndGoToNext { get; set; } + public string MainAdjustSetStart { get; set; } + public string MainAdjustSetStartKeepDuration { get; set; } + public string MainAdjustSetEnd { get; set; } + public string MainAdjustSelected100MsForward { get; set; } + public string MainAdjustSelected100MsBack { get; set; } + public string MainInsertAfter { get; set; } + public string MainTextBoxInsertAfter { get; set; } + public string MainTextBoxAutoBreak { get; set; } + public string MainTextBoxUnbreak { get; set; } + public string MainWaveformInsertAtCurrentPosition { get; set; } + public string MainInsertBefore { get; set; } + public string MainMergeDialog { get; set; } + public string MainToggleFocus { get; set; } + public string WaveformVerticalZoom { get; set; } + public string WaveformVerticalZoomOut { get; set; } + public string WaveformZoomIn { get; set; } + public string WaveformZoomOut { get; set; } + public string WaveformPlaySelection { get; set; } + public string WaveformSearchSilenceForward { get; set; } + public string WaveformSearchSilenceBack { get; set; } + public string WaveformAddTextHere { get; set; } + public string WaveformFocusListView { get; set; } + public string MainTranslateCustomSearch1 { get; set; } + public string MainTranslateCustomSearch2 { get; set; } + public string MainTranslateCustomSearch3 { get; set; } + public string MainTranslateCustomSearch4 { get; set; } + public string MainTranslateCustomSearch5 { get; set; } + public string MainTranslateCustomSearch6 { get; set; } + + public Shortcuts() + { + GeneralGoToFirstSelectedLine = "Control+L"; + GeneralMergeSelectedLines = "Control+Shift+M"; + GeneralToggleTranslationMode = "Control+Shift+O"; + GeneralSwitchOriginalAndTranslation = "Control+Alt+O"; + GeneralMergeOriginalAndTranslation = "Control+Alt+Shift+M"; + GeneralGoToNextSubtitle = "Shift+Return"; + GeneralGoToPrevSubtitle = string.Empty; + GeneralGoToStartOfCurrentSubtitle = string.Empty; + GeneralGoToEndOfCurrentSubtitle = string.Empty; + MainFileNew = "Control+N"; + MainFileOpen = "Control+O"; + MainFileSave = "Control+S"; + MainFileSaveOriginal = string.Empty; + MainFileSaveOriginalAs = string.Empty; + MainFileSaveAs = string.Empty; + MainFileSaveAll = string.Empty; + MainFileExportEbu = string.Empty; + MainEditUndo = "Control+Z"; + MainEditRedo = "Control+Y"; + MainEditFind = "Control+F"; + MainEditFindNext = "F3"; + MainEditReplace = "Control+H"; + MainEditMultipleReplace = "Control+Alt+M"; + MainEditGoToLineNumber = "Control+G"; + MainEditRightToLeft = "Control+Shift+Alt+R"; + MainEditInverseSelection = "Control+Shift+I"; + MainToolsFixCommonErrors = "Control+Shift+F"; + MainToolsFixCommonErrorsPreview = "Control+P"; + MainToolsMergeShortLines = string.Empty; + MainToolsSplitLongLines = string.Empty; + MainToolsRenumber = "Control+Shift+N"; + MainToolsRemoveTextForHI = "Control+Shift+H"; + MainToolsChangeCasing = "Control+Shift+C"; + MainVideoPlayPauseToggle = "Control+P"; + MainVideoPause = "Control+Alt+P"; + MainVideoShowHideVideo = "Control+Q"; + MainVideo1FrameLeft = string.Empty; + MainVideo1FrameRight = string.Empty; + MainVideo100MsLeft = string.Empty; + MainVideo100MsRight = string.Empty; + MainVideo500MsLeft = "Alt+Left"; + MainVideo500MsRight = "Alt+Right"; + MainVideo1000MsLeft = string.Empty; + MainVideo1000MsRight = string.Empty; + MainVideoFullscreen = "Alt+Return"; + MainSpellCheck = "Control+Shift+S"; + MainSpellCheckFindDoubleWords = "Control+Shift+D"; + MainSpellCheckAddWordToNames = "Control+Shift+L"; + MainSynchronizationAdjustTimes = "Control+Shift+A"; + MainSynchronizationVisualSync = "Control+Shift+V"; + MainSynchronizationPointSync = "Control+Shift+P"; + MainSynchronizationChangeFrameRate = string.Empty; + MainListViewItalic = "Control+I"; + MainEditReverseStartAndEndingForRTL = string.Empty; + MainTextBoxItalic = "Control+I"; + MainTextBoxSplitAtCursor = "Control+Alt+V"; + MainToolsAutoDuration = string.Empty; + MainTextBoxSelectionToLower = "Control+U"; + MainTextBoxSelectionToUpper = "Control+Shift+U"; + MainTextBoxToggleAutoDuration = string.Empty; + MainToolsBeamer = "Control+Shift+Alt+B"; + MainCreateInsertSubAtVideoPos = string.Empty; + MainCreatePlayFromJustBefore = string.Empty; + MainCreateSetStart = string.Empty; + MainCreateSetEnd = string.Empty; + MainCreateSetEndAddNewAndGoToNew = string.Empty; + MainCreateStartDownEndUp = string.Empty; + MainAdjustSetStartAndOffsetTheRest = "Control+Space"; + MainAdjustSetEndAndOffsetTheRest = string.Empty; + MainAdjustSetEndAndOffsetTheRestAndGoToNext = string.Empty; + MainAdjustSetEndAndGotoNext = string.Empty; + MainAdjustViaEndAutoStartAndGoToNext = string.Empty; + MainAdjustSetStartAutoDurationAndGoToNext = string.Empty; + MainAdjustSetEndNextStartAndGoToNext = string.Empty; + MainAdjustStartDownEndUpAndGoToNext = string.Empty; + MainAdjustSetStart = string.Empty; + MainAdjustSetStartKeepDuration = string.Empty; + MainAdjustSetEnd = string.Empty; + MainAdjustSelected100MsForward = string.Empty; + MainAdjustSelected100MsBack = string.Empty; + MainInsertAfter = "Alt+Insert"; + MainWaveformInsertAtCurrentPosition = "Insert"; + MainInsertBefore = "Control+Shift+Insert"; + MainTextBoxInsertAfter = "Alt+Insert"; + MainTextBoxAutoBreak = "Control+R"; + MainTextBoxUnbreak = string.Empty; + MainMergeDialog = string.Empty; + WaveformVerticalZoom = "Shift+Add"; + WaveformVerticalZoomOut = "Shift+Subtract"; + WaveformPlaySelection = string.Empty; + GeneralPlayFirstSelected = string.Empty; + WaveformSearchSilenceForward = string.Empty; + WaveformSearchSilenceBack = string.Empty; + WaveformAddTextHere = string.Empty; + } + } + + public class RemoveTextForHearingImpairedSettings + { + public bool RemoveTextBetweenBrackets { get; set; } + public bool RemoveTextBetweenParentheses { get; set; } + public bool RemoveTextBetweenCurlyBrackets { get; set; } + public bool RemoveTextBetweenQuestionMarks { get; set; } + public bool RemoveTextBetweenCustom { get; set; } + public string RemoveTextBetweenCustomBefore { get; set; } + public string RemoveTextBetweenCustomAfter { get; set; } + public bool RemoveTextBetweenOnlySeperateLines { get; set; } + public bool RemoveTextBeforeColon { get; set; } + public bool RemoveTextBeforeColonOnlyIfUppercase { get; set; } + public bool RemoveTextBeforeColonOnlyOnSeparateLine { get; set; } + public bool RemoveInterjections { get; set; } + public bool RemoveIfContains { get; set; } + public bool RemoveIfAllUppercase { get; set; } + public string RemoveIfContainsText { get; set; } + + public RemoveTextForHearingImpairedSettings() + { + RemoveTextBetweenBrackets = true; + RemoveTextBetweenParentheses = true; + RemoveTextBetweenCurlyBrackets = true; + RemoveTextBetweenQuestionMarks = true; + RemoveTextBetweenCustom = false; + RemoveTextBetweenCustomBefore = "¶"; + RemoveTextBetweenCustomAfter = "¶"; + RemoveTextBeforeColon = true; + RemoveTextBeforeColonOnlyIfUppercase = true; + RemoveIfContainsText = "¶"; + } + } + + public class SubtitleBeaming + { + public string FontName { get; set; } + public int FontSize { get; set; } + public Color FontColor { get; set; } + public Color BorderColor { get; set; } + public int BorderWidth { get; set; } + + public SubtitleBeaming() + { + FontName = "Verdana"; + FontSize = 30; + FontColor = Color.White; + BorderColor = Color.DarkGray; + BorderWidth = 2; + } + } + + public class Settings + { + public RecentFilesSettings RecentFiles { get; set; } + public GeneralSettings General { get; set; } + public ToolsSettings Tools { get; set; } + public SubtitleSettings SubtitleSettings { get; set; } + public ProxySettings Proxy { get; set; } + public WordListSettings WordLists { get; set; } + public FixCommonErrorsSettings CommonErrors { get; set; } + public VobSubOcrSettings VobSubOcr { get; set; } + public VideoControlsSettings VideoControls { get; set; } + public NetworkSettings NetworkSettings { get; set; } + public Shortcuts Shortcuts { get; set; } + public RemoveTextForHearingImpairedSettings RemoveTextForHearingImpaired { get; set; } + public SubtitleBeaming SubtitleBeaming { get; set; } + + [XmlArrayItem("MultipleSearchAndReplaceItem")] + public List MultipleSearchAndReplaceList { get; set; } + + [XmlIgnore] + public Language Language { get; set; } + + private Settings() + { + RecentFiles = new RecentFilesSettings(); + General = new GeneralSettings(); + Tools = new ToolsSettings(); + WordLists = new WordListSettings(); + SubtitleSettings = new SubtitleSettings(); + Proxy = new ProxySettings(); + CommonErrors = new FixCommonErrorsSettings(); + VobSubOcr = new VobSubOcrSettings(); + VideoControls = new VideoControlsSettings(); + NetworkSettings = new NetworkSettings(); + MultipleSearchAndReplaceList = new List(); + Language = new Language(); + Shortcuts = new Shortcuts(); + RemoveTextForHearingImpaired = new RemoveTextForHearingImpairedSettings(); + SubtitleBeaming = new SubtitleBeaming(); + } + + public void Save() + { + //this is too slow: Serialize(Configuration.SettingsFileName, this); + + CustomSerialize(Configuration.SettingsFileName, this); + } + + //private static void Serialize(string fileName, Settings settings) + //{ + // var s = new XmlSerializer(typeof(Settings)); + // TextWriter w = new StreamWriter(fileName); + // s.Serialize(w, settings); + // w.Close(); + //} + + public static Settings GetSettings() + { + var settings = new Settings(); + var settingsFileName = Configuration.SettingsFileName; + if (File.Exists(settingsFileName)) + { + try + { + //too slow... :( - settings = Deserialize(settingsFileName); // 688 msecs + settings = CustomDeserialize(settingsFileName); // 15 msecs + + if (settings.General.AutoConvertToUtf8) + settings.General.DefaultEncoding = Encoding.UTF8.EncodingName; + } + catch + { + settings = new Settings(); + } + + if (!string.IsNullOrEmpty(settings.General.ListViewLineSeparatorString)) + settings.General.ListViewLineSeparatorString = settings.General.ListViewLineSeparatorString.Replace("\n", string.Empty).Replace("\r", string.Empty); + + if (string.IsNullOrWhiteSpace(settings.General.ListViewLineSeparatorString)) + settings.General.ListViewLineSeparatorString = "
"; + + if (settings.Shortcuts.GeneralToggleTranslationMode == "Control+U" && settings.Shortcuts.MainTextBoxSelectionToLower == "Control+U") + { + settings.Shortcuts.GeneralToggleTranslationMode = "Control+Shift+O"; + settings.Shortcuts.GeneralSwitchOriginalAndTranslation = "Control+Alt+O"; + } + } + + return settings; + } + + //private static Settings Deserialize(string fileName) + //{ + // var r = new StreamReader(fileName); + // var s = new XmlSerializer(typeof(Settings)); + // var settings = (Settings)s.Deserialize(r); + // r.Close(); + + // if (settings.RecentFiles == null) + // settings.RecentFiles = new RecentFilesSettings(); + // if (settings.General == null) + // settings.General = new GeneralSettings(); + // if (settings.SsaStyle == null) + // settings.SsaStyle = new SsaStyleSettings(); + // if (settings.CommonErrors == null) + // settings.CommonErrors = new FixCommonErrorsSettings(); + // if (settings.VideoControls == null) + // settings.VideoControls = new VideoControlsSettings(); + // if (settings.VobSubOcr == null) + // settings.VobSubOcr = new VobSubOcrSettings(); + // if (settings.MultipleSearchAndReplaceList == null) + // settings.MultipleSearchAndReplaceList = new List(); + // if (settings.NetworkSettings == null) + // settings.NetworkSettings = new NetworkSettings(); + // if (settings.Shortcuts == null) + // settings.Shortcuts = new Shortcuts(); + + // return settings; + //} + + /// + /// A faster serializer than xml serializer... which is insanely slow (first time)!!!! + /// This method is auto-generated with XmlSerializerGenerator + /// + /// File name of xml settings file to load + /// Newly loaded settings + private static Settings CustomDeserialize(string fileName) + { + var doc = new XmlDocument { PreserveWhitespace = true }; + + var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + doc.Load(stream); + stream.Close(); + + var settings = new Settings(); + + settings.RecentFiles = new RecentFilesSettings(); + XmlNode node = doc.DocumentElement.SelectSingleNode("RecentFiles"); + foreach (XmlNode listNode in node.SelectNodes("FileNames/FileName")) + { + string firstVisibleIndex = "-1"; + if (listNode.Attributes["FirstVisibleIndex"] != null) + firstVisibleIndex = listNode.Attributes["FirstVisibleIndex"].Value; + + string firstSelectedIndex = "-1"; + if (listNode.Attributes["FirstSelectedIndex"] != null) + firstSelectedIndex = listNode.Attributes["FirstSelectedIndex"].Value; + + string videoFileName = null; + if (listNode.Attributes["VideoFileName"] != null) + videoFileName = listNode.Attributes["VideoFileName"].Value; + + string originalFileName = null; + if (listNode.Attributes["OriginalFileName"] != null) + originalFileName = listNode.Attributes["OriginalFileName"].Value; + + settings.RecentFiles.Files.Add(new RecentFileEntry { FileName = listNode.InnerText, FirstVisibleIndex = int.Parse(firstVisibleIndex), FirstSelectedIndex = int.Parse(firstSelectedIndex), VideoFileName = videoFileName, OriginalFileName = originalFileName }); + } + + settings.General = new GeneralSettings(); + node = doc.DocumentElement.SelectSingleNode("General"); + XmlNode subNode = node.SelectSingleNode("ShowToolbarNew"); + if (subNode != null) + settings.General.ShowToolbarNew = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("ShowToolbarOpen"); + if (subNode != null) + settings.General.ShowToolbarOpen = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("ShowToolbarSave"); + if (subNode != null) + settings.General.ShowToolbarSave = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("ShowToolbarSaveAs"); + if (subNode != null) + settings.General.ShowToolbarSaveAs = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("ShowToolbarFind"); + if (subNode != null) + settings.General.ShowToolbarFind = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("ShowToolbarReplace"); + if (subNode != null) + settings.General.ShowToolbarReplace = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("ShowToolbarFixCommonErrors"); + if (subNode != null) + settings.General.ShowToolbarFixCommonErrors = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("ShowToolbarVisualSync"); + if (subNode != null) + settings.General.ShowToolbarVisualSync = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("ShowToolbarSpellCheck"); + if (subNode != null) + settings.General.ShowToolbarSpellCheck = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("ShowToolbarSettings"); + if (subNode != null) + settings.General.ShowToolbarSettings = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("ShowToolbarHelp"); + if (subNode != null) + settings.General.ShowToolbarHelp = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("ShowFrameRate"); + if (subNode != null) + settings.General.ShowFrameRate = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("ShowVideoPlayer"); + if (subNode != null) + settings.General.ShowVideoPlayer = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("ShowAudioVisualizer"); + if (subNode != null) + settings.General.ShowAudioVisualizer = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("ShowWaveform"); + if (subNode != null) + settings.General.ShowWaveform = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("ShowSpectrogram"); + if (subNode != null) + settings.General.ShowSpectrogram = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("DefaultFrameRate"); + if (subNode != null) + { + settings.General.DefaultFrameRate = Convert.ToDouble(subNode.InnerText, CultureInfo.InvariantCulture); + if (settings.General.DefaultFrameRate > 23975) + settings.General.DefaultFrameRate = 23.976; + settings.General.CurrentFrameRate = settings.General.DefaultFrameRate; + } + subNode = node.SelectSingleNode("DefaultSubtitleFormat"); + if (subNode != null) + settings.General.DefaultSubtitleFormat = subNode.InnerText; + subNode = node.SelectSingleNode("DefaultEncoding"); + if (subNode != null) + settings.General.DefaultEncoding = subNode.InnerText; + subNode = node.SelectSingleNode("AutoConvertToUtf8"); + if (subNode != null) + settings.General.AutoConvertToUtf8 = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("AutoGuessAnsiEncoding"); + if (subNode != null) + settings.General.AutoGuessAnsiEncoding = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("_subtitleFontName"); + if (subNode != null) + settings.General.SubtitleFontName = subNode.InnerText; + subNode = node.SelectSingleNode("SubtitleFontSize"); + if (subNode != null) + settings.General.SubtitleFontSize = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("SubtitleFontBold"); + if (subNode != null) + settings.General.SubtitleFontBold = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("SubtitleFontColor"); + if (subNode != null) + settings.General.SubtitleFontColor = Color.FromArgb(Convert.ToInt32(subNode.InnerText)); + subNode = node.SelectSingleNode("SubtitleBackgroundColor"); + if (subNode != null) + settings.General.SubtitleBackgroundColor = Color.FromArgb(Convert.ToInt32(subNode.InnerText)); + subNode = node.SelectSingleNode("CenterSubtitleInTextBox"); + if (subNode != null) + settings.General.CenterSubtitleInTextBox = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("ShowRecentFiles"); + if (subNode != null) + settings.General.ShowRecentFiles = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("RememberSelectedLine"); + if (subNode != null) + settings.General.RememberSelectedLine = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("StartLoadLastFile"); + if (subNode != null) + settings.General.StartLoadLastFile = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("StartRememberPositionAndSize"); + if (subNode != null) + settings.General.StartRememberPositionAndSize = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("StartPosition"); + if (subNode != null) + settings.General.StartPosition = subNode.InnerText; + subNode = node.SelectSingleNode("StartSize"); + if (subNode != null) + settings.General.StartSize = subNode.InnerText; + subNode = node.SelectSingleNode("SplitContainerMainSplitterDistance"); + if (subNode != null) + settings.General.SplitContainerMainSplitterDistance = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("SplitContainer1SplitterDistance"); + if (subNode != null) + settings.General.SplitContainer1SplitterDistance = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("SplitContainerListViewAndTextSplitterDistance"); + if (subNode != null) + settings.General.SplitContainerListViewAndTextSplitterDistance = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("StartInSourceView"); + if (subNode != null) + settings.General.StartInSourceView = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("RemoveBlankLinesWhenOpening"); + if (subNode != null) + settings.General.RemoveBlankLinesWhenOpening = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("SubtitleLineMaximumLength"); + if (subNode != null) + settings.General.SubtitleLineMaximumLength = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("SubtitleMinimumDisplayMilliseconds"); + if (subNode != null) + settings.General.SubtitleMinimumDisplayMilliseconds = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("SubtitleMaximumDisplayMilliseconds"); + if (subNode != null) + settings.General.SubtitleMaximumDisplayMilliseconds = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("MinimumMillisecondsBetweenLines"); + if (subNode == null) // TODO: Remove in 3.5 + subNode = node.SelectSingleNode("MininumMillisecondsBetweenLines"); + if (subNode != null) + settings.General.MinimumMillisecondsBetweenLines = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("SetStartEndHumanDelay"); + if (subNode != null) + settings.General.SetStartEndHumanDelay = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("AutoWrapLineWhileTyping"); + if (subNode != null) + settings.General.AutoWrapLineWhileTyping = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("SubtitleMaximumCharactersPerSeconds"); + if (subNode != null) + settings.General.SubtitleMaximumCharactersPerSeconds = Convert.ToDouble(subNode.InnerText, CultureInfo.InvariantCulture); + subNode = node.SelectSingleNode("SubtitleOptimalCharactersPerSeconds"); + if (subNode != null) + settings.General.SubtitleOptimalCharactersPerSeconds = Convert.ToDouble(subNode.InnerText, CultureInfo.InvariantCulture); + subNode = node.SelectSingleNode("SpellCheckLanguage"); + if (subNode != null) + settings.General.SpellCheckLanguage = subNode.InnerText; + subNode = node.SelectSingleNode("VideoPlayer"); + if (subNode != null) + settings.General.VideoPlayer = subNode.InnerText; + subNode = node.SelectSingleNode("VideoPlayerDefaultVolume"); + if (subNode != null) + settings.General.VideoPlayerDefaultVolume = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("VideoPlayerPreviewFontSize"); + if (subNode != null) + settings.General.VideoPlayerPreviewFontSize = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("VideoPlayerPreviewFontBold"); + if (subNode != null) + settings.General.VideoPlayerPreviewFontBold = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("VideoPlayerShowStopButton"); + if (subNode != null) + settings.General.VideoPlayerShowStopButton = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("VideoPlayerShowMuteButton"); + if (subNode != null) + settings.General.VideoPlayerShowMuteButton = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("VideoPlayerShowFullscreenButton"); + if (subNode != null) + settings.General.VideoPlayerShowFullscreenButton = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("Language"); + if (subNode != null) + settings.General.Language = subNode.InnerText; + subNode = node.SelectSingleNode("ListViewLineSeparatorString"); + if (subNode != null) + settings.General.ListViewLineSeparatorString = subNode.InnerText; + subNode = node.SelectSingleNode("ListViewDoubleClickAction"); + if (subNode != null) + settings.General.ListViewDoubleClickAction = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("UppercaseLetters"); + if (subNode != null) + settings.General.UppercaseLetters = subNode.InnerText; + subNode = node.SelectSingleNode("DefaultAdjustMilliseconds"); + if (subNode != null) + settings.General.DefaultAdjustMilliseconds = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("AutoRepeatOn"); + if (subNode != null) + settings.General.AutoRepeatOn = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("AutoRepeatCount"); + if (subNode != null) + settings.General.AutoRepeatCount = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("SyncListViewWithVideoWhilePlaying"); + if (subNode != null) + settings.General.SyncListViewWithVideoWhilePlaying = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("AutoContinueOn"); + if (subNode != null) + settings.General.AutoContinueOn = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("AutoBackupSeconds"); + if (subNode != null) + settings.General.AutoBackupSeconds = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("SpellChecker"); + if (subNode != null) + settings.General.SpellChecker = subNode.InnerText; + subNode = node.SelectSingleNode("AllowEditOfOriginalSubtitle"); + if (subNode != null) + settings.General.AllowEditOfOriginalSubtitle = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("PromptDeleteLines"); + if (subNode != null) + settings.General.PromptDeleteLines = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("Undocked"); + if (subNode != null) + settings.General.Undocked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("UndockedVideoPosition"); + if (subNode != null) + settings.General.UndockedVideoPosition = subNode.InnerText; + subNode = node.SelectSingleNode("UndockedWaveformPosition"); + if (subNode != null) + settings.General.UndockedWaveformPosition = subNode.InnerText; + subNode = node.SelectSingleNode("UndockedVideoControlsPosition"); + if (subNode != null) + settings.General.UndockedVideoControlsPosition = subNode.InnerText; + subNode = node.SelectSingleNode("WaveformCenter"); + if (subNode != null) + settings.General.WaveformCenter = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("SmallDelayMilliseconds"); + if (subNode != null) + settings.General.SmallDelayMilliseconds = Convert.ToInt32((subNode.InnerText)); + subNode = node.SelectSingleNode("LargeDelayMilliseconds"); + if (subNode != null) + settings.General.LargeDelayMilliseconds = Convert.ToInt32((subNode.InnerText)); + subNode = node.SelectSingleNode("ShowOriginalAsPreviewIfAvailable"); + if (subNode != null) + settings.General.ShowOriginalAsPreviewIfAvailable = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("LastPacCodePage"); + if (subNode != null) + settings.General.LastPacCodePage = Convert.ToInt32((subNode.InnerText)); + subNode = node.SelectSingleNode("OpenSubtitleExtraExtensions"); + if (subNode != null) + settings.General.OpenSubtitleExtraExtensions = subNode.InnerText.Trim(); + subNode = node.SelectSingleNode("ListViewColumnsRememberSize"); + if (subNode != null) + settings.General.ListViewColumnsRememberSize = Convert.ToBoolean(subNode.InnerText.Trim()); + subNode = node.SelectSingleNode("ListViewNumberWidth"); + if (subNode != null) + settings.General.ListViewNumberWidth = Convert.ToInt32(subNode.InnerText.Trim()); + subNode = node.SelectSingleNode("ListViewStartWidth"); + if (subNode != null) + settings.General.ListViewStartWidth = Convert.ToInt32(subNode.InnerText.Trim()); + subNode = node.SelectSingleNode("ListViewEndWidth"); + if (subNode != null) + settings.General.ListViewEndWidth = Convert.ToInt32(subNode.InnerText.Trim()); + subNode = node.SelectSingleNode("ListViewDurationWidth"); + if (subNode != null) + settings.General.ListViewDurationWidth = Convert.ToInt32(subNode.InnerText.Trim()); + subNode = node.SelectSingleNode("ListViewTextWidth"); + if (subNode != null) + settings.General.ListViewTextWidth = Convert.ToInt32(subNode.InnerText.Trim()); + subNode = node.SelectSingleNode("VlcWaveTranscodeSettings"); + if (subNode != null) + settings.General.VlcWaveTranscodeSettings = subNode.InnerText.Trim(); + subNode = node.SelectSingleNode("VlcLocation"); + if (subNode != null) + settings.General.VlcLocation = subNode.InnerText.Trim(); + subNode = node.SelectSingleNode("VlcLocationRelative"); + if (subNode != null) + settings.General.VlcLocationRelative = subNode.InnerText.Trim(); + subNode = node.SelectSingleNode("MpcHcLocation"); + if (subNode != null) + settings.General.MpcHcLocation = subNode.InnerText.Trim(); + subNode = node.SelectSingleNode("UseFFmpegForWaveExtraction"); + if (subNode != null) + settings.General.UseFFmpegForWaveExtraction = Convert.ToBoolean(subNode.InnerText.Trim()); + subNode = node.SelectSingleNode("FFmpegLocation"); + if (subNode != null) + settings.General.FFmpegLocation = subNode.InnerText.Trim(); + subNode = node.SelectSingleNode("UseTimeFormatHHMMSSFF"); + if (subNode != null) + settings.General.UseTimeFormatHHMMSSFF = Convert.ToBoolean(subNode.InnerText.Trim()); + subNode = node.SelectSingleNode("ClearStatusBarAfterSeconds"); + if (subNode != null) + settings.General.ClearStatusBarAfterSeconds = Convert.ToInt32(subNode.InnerText.Trim()); + subNode = node.SelectSingleNode("Company"); + if (subNode != null) + settings.General.Company = subNode.InnerText; + subNode = node.SelectSingleNode("DisableVideoAutoLoading"); + if (subNode != null) + settings.General.DisableVideoAutoLoading = Convert.ToBoolean(subNode.InnerText.Trim()); + subNode = node.SelectSingleNode("RightToLeftMode"); + if (subNode != null) + settings.General.RightToLeftMode = Convert.ToBoolean(subNode.InnerText.Trim()); + subNode = node.SelectSingleNode("LastSaveAsFormat"); + if (subNode != null) + settings.General.LastSaveAsFormat = subNode.InnerText.Trim(); + subNode = node.SelectSingleNode("CheckForUpdates"); + if (subNode != null) + settings.General.CheckForUpdates = Convert.ToBoolean(subNode.InnerText.Trim()); + subNode = node.SelectSingleNode("LastCheckForUpdates"); + if (subNode != null) + settings.General.LastCheckForUpdates = Convert.ToDateTime(subNode.InnerText.Trim()); + subNode = node.SelectSingleNode("ShowProgress"); + if (subNode != null) + settings.General.ShowProgress = Convert.ToBoolean(subNode.InnerText.Trim()); + subNode = node.SelectSingleNode("ShowBetaStuff"); + if (subNode != null) + settings.General.ShowBetaStuff = Convert.ToBoolean(subNode.InnerText.Trim()); + subNode = node.SelectSingleNode("NewEmptyDefaultMs"); + if (subNode != null) + settings.General.NewEmptyDefaultMs = Convert.ToInt32(subNode.InnerText.Trim()); + subNode = node.SelectSingleNode("MoveVideo100Or500MsPlaySmallSample"); + if (subNode != null) + settings.General.MoveVideo100Or500MsPlaySmallSample = Convert.ToBoolean(subNode.InnerText.Trim()); + + settings.Tools = new ToolsSettings(); + node = doc.DocumentElement.SelectSingleNode("Tools"); + subNode = node.SelectSingleNode("StartSceneIndex"); + if (subNode != null) + settings.Tools.StartSceneIndex = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("EndSceneIndex"); + if (subNode != null) + settings.Tools.EndSceneIndex = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("VerifyPlaySeconds"); + if (subNode != null) + settings.Tools.VerifyPlaySeconds = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("MergeLinesShorterThan"); + if (subNode != null) + settings.Tools.MergeLinesShorterThan = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("FixShortDisplayTimesAllowMoveStartTime"); + if (subNode != null) + settings.Tools.FixShortDisplayTimesAllowMoveStartTime = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("MusicSymbol"); + if (subNode != null) + settings.Tools.MusicSymbol = subNode.InnerText; + subNode = node.SelectSingleNode("MusicSymbolToReplace"); + if (subNode != null) + settings.Tools.MusicSymbolToReplace = subNode.InnerText; + subNode = node.SelectSingleNode("UnicodeSymbolsToInsert"); + if (subNode != null) + settings.Tools.UnicodeSymbolsToInsert = subNode.InnerText; + subNode = node.SelectSingleNode("SpellCheckAutoChangeNames"); + if (subNode != null) + settings.Tools.SpellCheckAutoChangeNames = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("SpellCheckOneLetterWords"); + if (subNode != null) + settings.Tools.SpellCheckOneLetterWords = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("SpellCheckEnglishAllowInQuoteAsIng"); + if (subNode != null) + settings.Tools.SpellCheckEnglishAllowInQuoteAsIng = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("SpellCheckShowCompletedMessage"); + if (subNode != null) + settings.Tools.SpellCheckShowCompletedMessage = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("OcrFixUseHardcodedRules"); + if (subNode != null) + settings.Tools.OcrFixUseHardcodedRules = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("Interjections"); + if (subNode != null) + settings.Tools.Interjections = subNode.InnerText; + subNode = node.SelectSingleNode("MicrosoftBingApiId"); + if (subNode != null) + settings.Tools.MicrosoftBingApiId = subNode.InnerText; + subNode = node.SelectSingleNode("GoogleApiKey"); + if (subNode != null) + settings.Tools.GoogleApiKey = subNode.InnerText; + subNode = node.SelectSingleNode("UseGooleApiPaidService"); + if (subNode != null) + settings.Tools.UseGooleApiPaidService = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("GoogleTranslateLastTargetLanguage"); + if (subNode != null) + settings.Tools.GoogleTranslateLastTargetLanguage = subNode.InnerText; + subNode = node.SelectSingleNode("ListViewSyntaxColorDurationSmall"); + if (subNode != null) + settings.Tools.ListViewSyntaxColorDurationSmall = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("ListViewSyntaxColorDurationBig"); + if (subNode != null) + settings.Tools.ListViewSyntaxColorDurationBig = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("ListViewSyntaxColorLongLines"); + if (subNode != null) + settings.Tools.ListViewSyntaxColorLongLines = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("ListViewSyntaxMoreThanXLines"); + if (subNode != null) + settings.Tools.ListViewSyntaxMoreThanXLines = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("ListViewSyntaxMoreThanXLinesX"); + if (subNode != null) + settings.Tools.ListViewSyntaxMoreThanXLinesX = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("ListViewSyntaxColorOverlap"); + if (subNode != null) + settings.Tools.ListViewSyntaxColorOverlap = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("ListViewSyntaxErrorColor"); + if (subNode != null) + settings.Tools.ListViewSyntaxErrorColor = Color.FromArgb(int.Parse(subNode.InnerText)); + subNode = node.SelectSingleNode("ListViewUnfocusedSelectedColor"); + if (subNode != null) + settings.Tools.ListViewUnfocusedSelectedColor = Color.FromArgb(int.Parse(subNode.InnerText)); + subNode = node.SelectSingleNode("SplitAdvanced"); + if (subNode != null) + settings.Tools.SplitAdvanced = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("SplitOutputFolder"); + if (subNode != null) + settings.Tools.SplitOutputFolder = subNode.InnerText; + subNode = node.SelectSingleNode("SplitNumberOfParts"); + if (subNode != null) + settings.Tools.SplitNumberOfParts = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("SplitVia"); + if (subNode != null) + settings.Tools.SplitVia = subNode.InnerText; + subNode = node.SelectSingleNode("NewEmptyTranslationText"); + if (subNode != null) + settings.Tools.NewEmptyTranslationText = subNode.InnerText; + subNode = node.SelectSingleNode("BatchConvertOutputFolder"); + if (subNode != null) + settings.Tools.BatchConvertOutputFolder = subNode.InnerText; + subNode = node.SelectSingleNode("BatchConvertOverwriteExisting"); + if (subNode != null) + settings.Tools.BatchConvertOverwriteExisting = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("BatchConvertOverwriteOriginal"); + if (subNode != null) + settings.Tools.BatchConvertOverwriteOriginal = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("BatchConvertRemoveFormatting"); + if (subNode != null) + settings.Tools.BatchConvertRemoveFormatting = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("BatchConvertFixCasing"); + if (subNode != null) + settings.Tools.BatchConvertFixCasing = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("BatchConvertRemoveTextForHI"); + if (subNode != null) + settings.Tools.BatchConvertRemoveTextForHI = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("BatchConvertFixCommonErrors"); + if (subNode != null) + settings.Tools.BatchConvertFixCommonErrors = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("BatchConvertMultipleReplace"); + if (subNode != null) + settings.Tools.BatchConvertMultipleReplace = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("BatchConvertAutoBalance"); + if (subNode != null) + settings.Tools.BatchConvertAutoBalance = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("BatchConvertSplitLongLines"); + if (subNode != null) + settings.Tools.BatchConvertSplitLongLines = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("BatchConvertSetMinDisplayTimeBetweenSubtitles"); + if (subNode != null) + settings.Tools.BatchConvertSetMinDisplayTimeBetweenSubtitles = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("BatchConvertLanguage"); + if (subNode != null) + settings.Tools.BatchConvertLanguage = subNode.InnerText; + subNode = node.SelectSingleNode("BatchConvertFormat"); + if (subNode != null) + settings.Tools.BatchConvertFormat = subNode.InnerText; + subNode = node.SelectSingleNode("ModifySelectionRule"); + if (subNode != null) + settings.Tools.ModifySelectionRule = subNode.InnerText; + subNode = node.SelectSingleNode("ModifySelectionText"); + if (subNode != null) + settings.Tools.ModifySelectionText = subNode.InnerText; + subNode = node.SelectSingleNode("ModifySelectionCaseSensitive"); + if (subNode != null) + settings.Tools.ModifySelectionCaseSensitive = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("ExportVobSubFontName"); + if (subNode != null) + settings.Tools.ExportVobSubFontName = subNode.InnerText; + subNode = node.SelectSingleNode("ExportVobSubFontSize"); + if (subNode != null) + settings.Tools.ExportVobSubFontSize = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("ExportVobSubVideoResolution"); + if (subNode != null) + settings.Tools.ExportVobSubVideoResolution = subNode.InnerText; + subNode = node.SelectSingleNode("ExportVobSubSimpleRendering"); + if (subNode != null) + settings.Tools.ExportVobSubSimpleRendering = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("ExportVobAntiAliasingWithTransparency"); + if (subNode != null) + settings.Tools.ExportVobAntiAliasingWithTransparency = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("ExportVobSubLanguage"); + if (subNode != null) + settings.Tools.ExportVobSubLanguage = subNode.InnerText; + subNode = node.SelectSingleNode("ExportBluRayFontName"); + if (subNode != null) + settings.Tools.ExportBluRayFontName = subNode.InnerText; + subNode = node.SelectSingleNode("ExportBluRayFontSize"); + if (subNode != null) + settings.Tools.ExportBluRayFontSize = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("ExportFcpFontName"); + if (subNode != null) + settings.Tools.ExportFcpFontName = subNode.InnerText; + subNode = node.SelectSingleNode("ExportFontNameOther"); + if (subNode != null) + settings.Tools.ExportFontNameOther = subNode.InnerText; + subNode = node.SelectSingleNode("ExportFcpFontSize"); + if (subNode != null) + settings.Tools.ExportFcpFontSize = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("ExportFcpImageType"); + if (subNode != null) + settings.Tools.ExportFcpImageType = subNode.InnerText; + subNode = node.SelectSingleNode("ExportBdnXmlImageType"); + if (subNode != null) + settings.Tools.ExportBdnXmlImageType = subNode.InnerText; + subNode = node.SelectSingleNode("ExportLastFontSize"); + if (subNode != null) + settings.Tools.ExportLastFontSize = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("ExportLastLineHeight"); + if (subNode != null) + settings.Tools.ExportLastLineHeight = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("ExportLastBorderWidth"); + if (subNode != null) + settings.Tools.ExportLastBorderWidth = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("ExportLastFontBold"); + if (subNode != null) + settings.Tools.ExportLastFontBold = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("ExportBluRayVideoResolution"); + if (subNode != null) + settings.Tools.ExportBluRayVideoResolution = subNode.InnerText; + subNode = node.SelectSingleNode("ExportFontColor"); + if (subNode != null) + settings.Tools.ExportFontColor = Color.FromArgb(int.Parse(subNode.InnerText)); + subNode = node.SelectSingleNode("ExportBorderColor"); + if (subNode != null) + settings.Tools.ExportBorderColor = Color.FromArgb(int.Parse(subNode.InnerText)); + subNode = node.SelectSingleNode("ExportShadowColor"); + if (subNode != null) + settings.Tools.ExportShadowColor = Color.FromArgb(int.Parse(subNode.InnerText)); + subNode = node.SelectSingleNode("ExportBottomMargin"); + if (subNode != null) + settings.Tools.ExportBottomMargin = int.Parse(subNode.InnerText); + subNode = node.SelectSingleNode("ExportHorizontalAlignment"); + if (subNode != null) + settings.Tools.ExportHorizontalAlignment = int.Parse(subNode.InnerText); + subNode = node.SelectSingleNode("ExportBluRayBottomMargin"); + if (subNode != null) + settings.Tools.ExportBluRayBottomMargin = int.Parse(subNode.InnerText); + subNode = node.SelectSingleNode("ExportBluRayShadow"); + if (subNode != null) + settings.Tools.ExportBluRayShadow = int.Parse(subNode.InnerText); + subNode = node.SelectSingleNode("Export3DType"); + if (subNode != null) + settings.Tools.Export3DType = int.Parse(subNode.InnerText); + subNode = node.SelectSingleNode("Export3DDepth"); + if (subNode != null) + settings.Tools.Export3DDepth = int.Parse(subNode.InnerText); + subNode = node.SelectSingleNode("ExportLastShadowTransparency"); + if (subNode != null) + settings.Tools.ExportLastShadowTransparency = int.Parse(subNode.InnerText, CultureInfo.InvariantCulture); + subNode = node.SelectSingleNode("ExportLastFrameRate"); + if (subNode != null) + settings.Tools.ExportLastFrameRate = double.Parse(subNode.InnerText, CultureInfo.InvariantCulture); + subNode = node.SelectSingleNode("ExportPenLineJoin"); + if (subNode != null) + settings.Tools.ExportPenLineJoin = subNode.InnerText; + subNode = node.SelectSingleNode("FixCommonErrorsFixOverlapAllowEqualEndStart"); + if (subNode != null) + settings.Tools.FixCommonErrorsFixOverlapAllowEqualEndStart = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("ImportTextSplitting"); + if (subNode != null) + settings.Tools.ImportTextSplitting = subNode.InnerText; + subNode = node.SelectSingleNode("ImportTextMergeShortLines"); + if (subNode != null) + settings.Tools.ImportTextMergeShortLines = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("ImportTextLineBreak"); + if (subNode != null) + settings.Tools.ImportTextLineBreak = subNode.InnerText; + subNode = node.SelectSingleNode("GenerateTimeCodePatterns"); + if (subNode != null) + settings.Tools.GenerateTimeCodePatterns = subNode.InnerText; + subNode = node.SelectSingleNode("MusicSymbolStyle"); + if (subNode != null) + settings.Tools.MusicSymbolStyle = subNode.InnerText; + subNode = node.SelectSingleNode("BridgeGapMilliseconds"); + if (subNode != null) + settings.Tools.BridgeGapMilliseconds = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("ExportCustomTemplates"); + if (subNode != null) + settings.Tools.ExportCustomTemplates = subNode.InnerText; + subNode = node.SelectSingleNode("ChangeCasingChoice"); + if (subNode != null) + settings.Tools.ChangeCasingChoice = subNode.InnerText; + subNode = node.SelectSingleNode("UseNoLineBreakAfter"); + if (subNode != null) + settings.Tools.UseNoLineBreakAfter = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("NoLineBreakAfterEnglish"); + if (subNode != null) + settings.Tools.NoLineBreakAfterEnglish = subNode.InnerText.Replace(" ", " "); + subNode = node.SelectSingleNode("FindHistory"); + if (subNode != null) + { + foreach (XmlNode findItem in subNode.ChildNodes) + { + if (findItem.Name == "Text") + { + settings.Tools.FindHistory.Add(findItem.InnerText); + } + } + } + + settings.SubtitleSettings = new SubtitleSettings(); + node = doc.DocumentElement.SelectSingleNode("SubtitleSettings"); + if (node != null) + { + subNode = node.SelectSingleNode("SsaFontName"); + if (subNode != null) + settings.SubtitleSettings.SsaFontName = subNode.InnerText; + subNode = node.SelectSingleNode("SsaFontSize"); + if (subNode != null) + settings.SubtitleSettings.SsaFontSize = Convert.ToDouble(subNode.InnerText, CultureInfo.InvariantCulture); + subNode = node.SelectSingleNode("SsaFontColorArgb"); + if (subNode != null) + settings.SubtitleSettings.SsaFontColorArgb = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("SsaOutline"); + if (subNode != null) + settings.SubtitleSettings.SsaOutline = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("SsaShadow"); + if (subNode != null) + settings.SubtitleSettings.SsaShadow = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("SsaOpaqueBox"); + if (subNode != null) + settings.SubtitleSettings.SsaOpaqueBox = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("DCinemaFontFile"); + if (subNode != null) + settings.SubtitleSettings.DCinemaFontFile = subNode.InnerText; + subNode = node.SelectSingleNode("DCinemaFontSize"); + if (subNode != null) + settings.SubtitleSettings.DCinemaFontSize = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("DCinemaBottomMargin"); + if (subNode != null) + settings.SubtitleSettings.DCinemaBottomMargin = Convert.ToInt32(subNode.InnerText, CultureInfo.InvariantCulture); + subNode = node.SelectSingleNode("DCinemaZPosition"); + if (subNode != null) + settings.SubtitleSettings.DCinemaZPosition = Convert.ToDouble(subNode.InnerText); + subNode = node.SelectSingleNode("DCinemaFadeUpTime"); + if (subNode != null) + settings.SubtitleSettings.DCinemaFadeUpTime = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("DCinemaFadeDownTime"); + if (subNode != null) + settings.SubtitleSettings.DCinemaFadeDownTime = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("SamiDisplayTwoClassesAsTwoSubtitles"); + if (subNode != null) + settings.SubtitleSettings.SamiDisplayTwoClassesAsTwoSubtitles = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("SamiHtmlEncodeMode"); + if (subNode != null) + settings.SubtitleSettings.SamiHtmlEncodeMode = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("TimedText10TimeCodeFormat"); + if (subNode != null) + settings.SubtitleSettings.TimedText10TimeCodeFormat = subNode.InnerText; + subNode = node.SelectSingleNode("FcpFontSize"); + if (subNode != null) + settings.SubtitleSettings.FcpFontSize = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("FcpFontName"); + if (subNode != null) + settings.SubtitleSettings.FcpFontName = subNode.InnerText; + subNode = node.SelectSingleNode("CheetahCaptionAlwayWriteEndTime"); + if (subNode != null) + settings.SubtitleSettings.CheetahCaptionAlwayWriteEndTime = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("NuendoCharacterListFile"); + if (subNode != null) + settings.SubtitleSettings.NuendoCharacterListFile = subNode.InnerText; + } + + settings.Proxy = new ProxySettings(); + node = doc.DocumentElement.SelectSingleNode("Proxy"); + subNode = node.SelectSingleNode("ProxyAddress"); + if (subNode != null) + settings.Proxy.ProxyAddress = subNode.InnerText; + subNode = node.SelectSingleNode("UserName"); + if (subNode != null) + settings.Proxy.UserName = subNode.InnerText; + subNode = node.SelectSingleNode("Password"); + if (subNode != null) + settings.Proxy.Password = subNode.InnerText; + subNode = node.SelectSingleNode("Domain"); + if (subNode != null) + settings.Proxy.Domain = subNode.InnerText; + + settings.WordLists = new WordListSettings(); + node = doc.DocumentElement.SelectSingleNode("WordLists"); + subNode = node.SelectSingleNode("LastLanguage"); + if (subNode != null) + settings.WordLists.LastLanguage = subNode.InnerText; + subNode = node.SelectSingleNode("NamesEtcUrl"); + if (subNode != null) + settings.WordLists.NamesEtcUrl = subNode.InnerText; + subNode = node.SelectSingleNode("UseOnlineNamesEtc"); + if (subNode != null) + settings.WordLists.UseOnlineNamesEtc = Convert.ToBoolean(subNode.InnerText); + + settings.CommonErrors = new FixCommonErrorsSettings(); + node = doc.DocumentElement.SelectSingleNode("CommonErrors"); + subNode = node.SelectSingleNode("StartPosition"); + if (subNode != null) + settings.CommonErrors.StartPosition = subNode.InnerText; + subNode = node.SelectSingleNode("StartSize"); + if (subNode != null) + settings.CommonErrors.StartSize = subNode.InnerText; + subNode = node.SelectSingleNode("EmptyLinesTicked"); + if (subNode != null) + settings.CommonErrors.EmptyLinesTicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("OverlappingDisplayTimeTicked"); + if (subNode != null) + settings.CommonErrors.OverlappingDisplayTimeTicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("TooShortDisplayTimeTicked"); + if (subNode != null) + settings.CommonErrors.TooShortDisplayTimeTicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("TooLongDisplayTimeTicked"); + if (subNode != null) + settings.CommonErrors.TooLongDisplayTimeTicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("InvalidItalicTagsTicked"); + if (subNode != null) + settings.CommonErrors.InvalidItalicTagsTicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("BreakLongLinesTicked"); + if (subNode != null) + settings.CommonErrors.BreakLongLinesTicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("MergeShortLinesTicked"); + if (subNode != null) + settings.CommonErrors.MergeShortLinesTicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("MergeShortLinesAllTicked"); + if (subNode != null) + settings.CommonErrors.MergeShortLinesAllTicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("UnneededSpacesTicked"); + if (subNode != null) + settings.CommonErrors.UnneededSpacesTicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("UnneededPeriodsTicked"); + if (subNode != null) + settings.CommonErrors.UnneededPeriodsTicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("MissingSpacesTicked"); + if (subNode != null) + settings.CommonErrors.MissingSpacesTicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("AddMissingQuotesTicked"); + if (subNode != null) + settings.CommonErrors.AddMissingQuotesTicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("Fix3PlusLinesTicked"); + if (subNode != null) + settings.CommonErrors.Fix3PlusLinesTicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("FixHyphensTicked"); + if (subNode != null) + settings.CommonErrors.FixHyphensTicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("FixHyphensAddTicked"); + if (subNode != null) + settings.CommonErrors.FixHyphensAddTicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("UppercaseIInsideLowercaseWordTicked"); + if (subNode != null) + settings.CommonErrors.UppercaseIInsideLowercaseWordTicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("DoubleApostropheToQuoteTicked"); + if (subNode != null) + settings.CommonErrors.DoubleApostropheToQuoteTicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("AddPeriodAfterParagraphTicked"); + if (subNode != null) + settings.CommonErrors.AddPeriodAfterParagraphTicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("StartWithUppercaseLetterAfterParagraphTicked"); + if (subNode != null) + settings.CommonErrors.StartWithUppercaseLetterAfterParagraphTicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("StartWithUppercaseLetterAfterPeriodInsideParagraphTicked"); + if (subNode != null) + settings.CommonErrors.StartWithUppercaseLetterAfterPeriodInsideParagraphTicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("StartWithUppercaseLetterAfterColonTicked"); + if (subNode != null) + settings.CommonErrors.StartWithUppercaseLetterAfterColonTicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("AloneLowercaseIToUppercaseIEnglishTicked"); + if (subNode != null) + settings.CommonErrors.AloneLowercaseIToUppercaseIEnglishTicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("FixOcrErrorsViaReplaceListTicked"); + if (subNode != null) + settings.CommonErrors.FixOcrErrorsViaReplaceListTicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("RemoveSpaceBetweenNumberTicked"); + if (subNode != null) + settings.CommonErrors.RemoveSpaceBetweenNumberTicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("FixDialogsOnOneLineTicked"); + if (subNode != null) + settings.CommonErrors.FixDialogsOnOneLineTicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("TurkishAnsiTicked"); + if (subNode != null) + settings.CommonErrors.TurkishAnsiTicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("DanishLetterITicked"); + if (subNode != null) + settings.CommonErrors.DanishLetterITicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("SpanishInvertedQuestionAndExclamationMarksTicked"); + if (subNode != null) + settings.CommonErrors.SpanishInvertedQuestionAndExclamationMarksTicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("FixDoubleDashTicked"); + if (subNode != null) + settings.CommonErrors.FixDoubleDashTicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("FixDoubleGreaterThanTicked"); + if (subNode != null) + settings.CommonErrors.FixDoubleGreaterThanTicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("FixEllipsesStartTicked"); + if (subNode != null) + settings.CommonErrors.FixEllipsesStartTicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("FixMissingOpenBracketTicked"); + if (subNode != null) + settings.CommonErrors.FixMissingOpenBracketTicked = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("FixMusicNotationTicked"); + if (subNode != null) + settings.CommonErrors.FixMusicNotationTicked = Convert.ToBoolean(subNode.InnerText); + + settings.VideoControls = new VideoControlsSettings(); + node = doc.DocumentElement.SelectSingleNode("VideoControls"); + subNode = node.SelectSingleNode("CustomSearchText1"); + if (subNode != null) + settings.VideoControls.CustomSearchText1 = subNode.InnerText; + subNode = node.SelectSingleNode("CustomSearchText2"); + if (subNode != null) + settings.VideoControls.CustomSearchText2 = subNode.InnerText; + subNode = node.SelectSingleNode("CustomSearchText3"); + if (subNode != null) + settings.VideoControls.CustomSearchText3 = subNode.InnerText; + subNode = node.SelectSingleNode("CustomSearchText4"); + if (subNode != null) + settings.VideoControls.CustomSearchText4 = subNode.InnerText; + subNode = node.SelectSingleNode("CustomSearchText5"); + if (subNode != null) + settings.VideoControls.CustomSearchText5 = subNode.InnerText; + subNode = node.SelectSingleNode("CustomSearchText6"); + if (subNode != null) + settings.VideoControls.CustomSearchText6 = subNode.InnerText; + subNode = node.SelectSingleNode("CustomSearchUrl1"); + if (subNode != null) + settings.VideoControls.CustomSearchUrl1 = subNode.InnerText; + subNode = node.SelectSingleNode("CustomSearchUrl1"); + if (subNode != null) + settings.VideoControls.CustomSearchUrl1 = subNode.InnerText; + subNode = node.SelectSingleNode("CustomSearchUrl2"); + if (subNode != null) + settings.VideoControls.CustomSearchUrl2 = subNode.InnerText; + subNode = node.SelectSingleNode("CustomSearchUrl3"); + if (subNode != null) + settings.VideoControls.CustomSearchUrl3 = subNode.InnerText; + subNode = node.SelectSingleNode("CustomSearchUrl4"); + if (subNode != null) + settings.VideoControls.CustomSearchUrl4 = subNode.InnerText; + subNode = node.SelectSingleNode("CustomSearchUrl5"); + if (subNode != null) + settings.VideoControls.CustomSearchUrl5 = subNode.InnerText; + subNode = node.SelectSingleNode("CustomSearchUrl6"); + if (subNode != null) + settings.VideoControls.CustomSearchUrl6 = subNode.InnerText; + subNode = node.SelectSingleNode("LastActiveTab"); + if (subNode != null) + settings.VideoControls.LastActiveTab = subNode.InnerText; + subNode = node.SelectSingleNode("WaveformDrawGrid"); + if (subNode != null) + settings.VideoControls.WaveformDrawGrid = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("WaveformAllowOverlap"); + if (subNode != null) + settings.VideoControls.WaveformAllowOverlap = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("WaveformFocusOnMouseEnter"); + if (subNode != null) + settings.VideoControls.WaveformFocusOnMouseEnter = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("WaveformListViewFocusOnMouseEnter"); + if (subNode != null) + settings.VideoControls.WaveformListViewFocusOnMouseEnter = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("WaveformBorderHitMs"); + if (subNode != null) + settings.VideoControls.WaveformBorderHitMs = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("WaveformGridColor"); + if (subNode != null) + settings.VideoControls.WaveformGridColor = Color.FromArgb(int.Parse(subNode.InnerText)); + subNode = node.SelectSingleNode("WaveformColor"); + if (subNode != null) + settings.VideoControls.WaveformColor = Color.FromArgb(int.Parse(subNode.InnerText)); + subNode = node.SelectSingleNode("WaveformSelectedColor"); + if (subNode != null) + settings.VideoControls.WaveformSelectedColor = Color.FromArgb(int.Parse(subNode.InnerText)); + subNode = node.SelectSingleNode("WaveformBackgroundColor"); + if (subNode != null) + settings.VideoControls.WaveformBackgroundColor = Color.FromArgb(int.Parse(subNode.InnerText)); + subNode = node.SelectSingleNode("WaveformTextColor"); + if (subNode != null) + settings.VideoControls.WaveformTextColor = Color.FromArgb(int.Parse(subNode.InnerText)); + subNode = node.SelectSingleNode("WaveformTextSize"); + if (subNode != null) + settings.VideoControls.WaveformTextSize = Convert.ToInt32(subNode.InnerText, CultureInfo.InvariantCulture); + subNode = node.SelectSingleNode("WaveformTextBold"); + if (subNode != null) + settings.VideoControls.WaveformTextBold = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("WaveformDoubleClickOnNonParagraphAction"); + if (subNode != null) + settings.VideoControls.WaveformDoubleClickOnNonParagraphAction = subNode.InnerText; + subNode = node.SelectSingleNode("WaveformRightClickOnNonParagraphAction"); + if (subNode != null) + settings.VideoControls.WaveformRightClickOnNonParagraphAction = subNode.InnerText; + subNode = node.SelectSingleNode("WaveformMouseWheelScrollUpIsForward"); + if (subNode != null) + settings.VideoControls.WaveformMouseWheelScrollUpIsForward = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("GenerateSpectrogram"); + if (subNode != null) + settings.VideoControls.GenerateSpectrogram = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("SpectrogramAppearance"); + if (subNode != null) + settings.VideoControls.SpectrogramAppearance = subNode.InnerText; + subNode = node.SelectSingleNode("WaveformMinimumSampleRate"); + if (subNode == null) // TODO: Remove in 3.5 + subNode = node.SelectSingleNode("WaveformMininumSampleRate"); + if (subNode != null) + settings.VideoControls.WaveformMinimumSampleRate = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("WaveformSeeksSilenceDurationSeconds"); + if (subNode != null) + settings.VideoControls.WaveformSeeksSilenceDurationSeconds = Convert.ToDouble(subNode.InnerText, CultureInfo.InvariantCulture); + subNode = node.SelectSingleNode("WaveformSeeksSilenceMaxVolume"); + if (subNode != null) + settings.VideoControls.WaveformSeeksSilenceMaxVolume = Convert.ToInt32(subNode.InnerText); + + settings.NetworkSettings = new NetworkSettings(); + node = doc.DocumentElement.SelectSingleNode("NetworkSettings"); + if (node != null) + { + subNode = node.SelectSingleNode("SessionKey"); + if (subNode != null) + settings.NetworkSettings.SessionKey = subNode.InnerText; + subNode = node.SelectSingleNode("UserName"); + if (subNode != null) + settings.NetworkSettings.UserName = subNode.InnerText; + subNode = node.SelectSingleNode("WebServiceUrl"); + if (subNode != null) + settings.NetworkSettings.WebServiceUrl = subNode.InnerText; + subNode = node.SelectSingleNode("PollIntervalSeconds"); + if (subNode != null) + settings.NetworkSettings.PollIntervalSeconds = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("NewMessageSound"); + if (subNode != null) + settings.NetworkSettings.NewMessageSound = subNode.InnerText; + } + + settings.VobSubOcr = new VobSubOcrSettings(); + node = doc.DocumentElement.SelectSingleNode("VobSubOcr"); + subNode = node.SelectSingleNode("XOrMorePixelsMakesSpace"); + if (subNode != null) + settings.VobSubOcr.XOrMorePixelsMakesSpace = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("AllowDifferenceInPercent"); + if (subNode != null) + settings.VobSubOcr.AllowDifferenceInPercent = Convert.ToDouble(subNode.InnerText, CultureInfo.InvariantCulture); + subNode = node.SelectSingleNode("BlurayAllowDifferenceInPercent"); + if (subNode != null) + settings.VobSubOcr.BlurayAllowDifferenceInPercent = Convert.ToDouble(subNode.InnerText, CultureInfo.InvariantCulture); + subNode = node.SelectSingleNode("LastImageCompareFolder"); + if (subNode != null) + settings.VobSubOcr.LastImageCompareFolder = subNode.InnerText; + subNode = node.SelectSingleNode("LastModiLanguageId"); + if (subNode != null) + settings.VobSubOcr.LastModiLanguageId = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("LastOcrMethod"); + if (subNode != null) + settings.VobSubOcr.LastOcrMethod = subNode.InnerText; + subNode = node.SelectSingleNode("TesseractLastLanguage"); + if (subNode != null) + settings.VobSubOcr.TesseractLastLanguage = subNode.InnerText; + subNode = node.SelectSingleNode("UseModiInTesseractForUnknownWords"); + if (subNode != null) + settings.VobSubOcr.UseModiInTesseractForUnknownWords = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("UseItalicsInTesseract"); + if (subNode != null) + settings.VobSubOcr.UseItalicsInTesseract = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("UseMusicSymbolsInTesseract"); + if (subNode != null) + settings.VobSubOcr.UseMusicSymbolsInTesseract = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("RightToLeft"); + if (subNode != null) + settings.VobSubOcr.RightToLeft = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("TopToBottom"); + if (subNode != null) + settings.VobSubOcr.TopToBottom = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("DefaultMillisecondsForUnknownDurations"); + if (subNode != null) + settings.VobSubOcr.DefaultMillisecondsForUnknownDurations = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("PromptForUnknownWords"); + if (subNode != null) + settings.VobSubOcr.PromptForUnknownWords = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("GuessUnknownWords"); + if (subNode != null) + settings.VobSubOcr.GuessUnknownWords = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("AutoBreakSubtitleIfMoreThanTwoLines"); + if (subNode != null) + settings.VobSubOcr.AutoBreakSubtitleIfMoreThanTwoLines = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("ItalicFactor"); + if (subNode != null) + settings.VobSubOcr.ItalicFactor = Convert.ToDouble(subNode.InnerText, CultureInfo.InvariantCulture); + subNode = node.SelectSingleNode("LineOcrDraw"); + if (subNode != null) + settings.VobSubOcr.LineOcrDraw = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("LineOcrAdvancedItalic"); + if (subNode != null) + settings.VobSubOcr.LineOcrAdvancedItalic = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("LineOcrLastLanguages"); + if (subNode != null) + settings.VobSubOcr.LineOcrLastLanguages = subNode.InnerText; + subNode = node.SelectSingleNode("LineOcrLastSpellCheck"); + if (subNode != null) + settings.VobSubOcr.LineOcrLastSpellCheck = subNode.InnerText; + subNode = node.SelectSingleNode("LineOcrXOrMorePixelsMakesSpace"); + if (subNode != null) + settings.VobSubOcr.LineOcrXOrMorePixelsMakesSpace = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("LineOcrMinLineHeight"); + if (subNode != null) + settings.VobSubOcr.LineOcrMinLineHeight = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("LineOcrMaxLineHeight"); + if (subNode != null) + settings.VobSubOcr.LineOcrMaxLineHeight = Convert.ToInt32(subNode.InnerText); + + foreach (XmlNode listNode in doc.DocumentElement.SelectNodes("MultipleSearchAndReplaceList/MultipleSearchAndReplaceItem")) + { + var item = new MultipleSearchAndReplaceSetting(); + subNode = listNode.SelectSingleNode("Enabled"); + if (subNode != null) + item.Enabled = Convert.ToBoolean(subNode.InnerText); + subNode = listNode.SelectSingleNode("FindWhat"); + if (subNode != null) + item.FindWhat = subNode.InnerText; + subNode = listNode.SelectSingleNode("ReplaceWith"); + if (subNode != null) + item.ReplaceWith = subNode.InnerText; + subNode = listNode.SelectSingleNode("SearchType"); + if (subNode != null) + item.SearchType = subNode.InnerText; + settings.MultipleSearchAndReplaceList.Add(item); + } + + settings.Shortcuts = new Shortcuts(); + node = doc.DocumentElement.SelectSingleNode("Shortcuts"); + if (node != null) + { + subNode = node.SelectSingleNode("GeneralGoToFirstSelectedLine"); + if (subNode != null) + settings.Shortcuts.GeneralGoToFirstSelectedLine = subNode.InnerText; + subNode = node.SelectSingleNode("GeneralGoToNextEmptyLine"); + if (subNode != null) + settings.Shortcuts.GeneralGoToNextEmptyLine = subNode.InnerText; + subNode = node.SelectSingleNode("GeneralMergeSelectedLines"); + if (subNode != null) + settings.Shortcuts.GeneralMergeSelectedLines = subNode.InnerText; + subNode = node.SelectSingleNode("GeneralMergeSelectedLinesOnlyFirstText"); + if (subNode != null) + settings.Shortcuts.GeneralMergeSelectedLinesOnlyFirstText = subNode.InnerText; + subNode = node.SelectSingleNode("GeneralToggleTranslationMode"); + if (subNode != null) + settings.Shortcuts.GeneralToggleTranslationMode = subNode.InnerText; + subNode = node.SelectSingleNode("GeneralSwitchOriginalAndTranslation"); + if (subNode != null) + settings.Shortcuts.GeneralSwitchOriginalAndTranslation = subNode.InnerText; + subNode = node.SelectSingleNode("GeneralMergeOriginalAndTranslation"); + if (subNode != null) + settings.Shortcuts.GeneralMergeOriginalAndTranslation = subNode.InnerText; + subNode = node.SelectSingleNode("GeneralGoToNextSubtitle"); + if (subNode != null) + settings.Shortcuts.GeneralGoToNextSubtitle = subNode.InnerText; + subNode = node.SelectSingleNode("GeneralGoToPrevSubtitle"); + if (subNode != null) + settings.Shortcuts.GeneralGoToPrevSubtitle = subNode.InnerText; + subNode = node.SelectSingleNode("GeneralGoToEndOfCurrentSubtitle"); + if (subNode != null) + settings.Shortcuts.GeneralGoToEndOfCurrentSubtitle = subNode.InnerText; + subNode = node.SelectSingleNode("GeneralGoToStartOfCurrentSubtitle"); + if (subNode != null) + settings.Shortcuts.GeneralGoToStartOfCurrentSubtitle = subNode.InnerText; + subNode = node.SelectSingleNode("GeneralPlayFirstSelected"); + if (subNode != null) + settings.Shortcuts.GeneralPlayFirstSelected = subNode.InnerText; + subNode = node.SelectSingleNode("MainFileNew"); + if (subNode != null) + settings.Shortcuts.MainFileNew = subNode.InnerText; + subNode = node.SelectSingleNode("MainFileOpen"); + if (subNode != null) + settings.Shortcuts.MainFileOpen = subNode.InnerText; + subNode = node.SelectSingleNode("MainFileOpenKeepVideo"); + if (subNode != null) + settings.Shortcuts.MainFileOpenKeepVideo = subNode.InnerText; + subNode = node.SelectSingleNode("MainFileSave"); + if (subNode != null) + settings.Shortcuts.MainFileSave = subNode.InnerText; + subNode = node.SelectSingleNode("MainFileSaveOriginal"); + if (subNode != null) + settings.Shortcuts.MainFileSaveOriginal = subNode.InnerText; + subNode = node.SelectSingleNode("MainFileSaveOriginalAs"); + if (subNode != null) + settings.Shortcuts.MainFileSaveOriginalAs = subNode.InnerText; + subNode = node.SelectSingleNode("MainFileSaveAs"); + if (subNode != null) + settings.Shortcuts.MainFileSaveAs = subNode.InnerText; + subNode = node.SelectSingleNode("MainFileSaveAll"); + if (subNode != null) + settings.Shortcuts.MainFileSaveAll = subNode.InnerText; + subNode = node.SelectSingleNode("MainFileExportEbu"); + if (subNode != null) + settings.Shortcuts.MainFileExportEbu = subNode.InnerText; + subNode = node.SelectSingleNode("MainEditUndo"); + if (subNode != null) + settings.Shortcuts.MainEditUndo = subNode.InnerText; + subNode = node.SelectSingleNode("MainEditRedo"); + if (subNode != null) + settings.Shortcuts.MainEditRedo = subNode.InnerText; + subNode = node.SelectSingleNode("MainEditFind"); + if (subNode != null) + settings.Shortcuts.MainEditFind = subNode.InnerText; + subNode = node.SelectSingleNode("MainEditFindNext"); + if (subNode != null) + settings.Shortcuts.MainEditFindNext = subNode.InnerText; + subNode = node.SelectSingleNode("MainEditReplace"); + if (subNode != null) + settings.Shortcuts.MainEditReplace = subNode.InnerText; + subNode = node.SelectSingleNode("MainEditMultipleReplace"); + if (subNode != null) + settings.Shortcuts.MainEditMultipleReplace = subNode.InnerText; + subNode = node.SelectSingleNode("MainEditGoToLineNumber"); + if (subNode != null) + settings.Shortcuts.MainEditGoToLineNumber = subNode.InnerText; + subNode = node.SelectSingleNode("MainEditRightToLeft"); + if (subNode != null) + settings.Shortcuts.MainEditRightToLeft = subNode.InnerText; + subNode = node.SelectSingleNode("MainToolsFixCommonErrors"); + if (subNode != null) + settings.Shortcuts.MainToolsFixCommonErrors = subNode.InnerText; + subNode = node.SelectSingleNode("MainToolsFixCommonErrorsPreview"); + if (subNode != null) + settings.Shortcuts.MainToolsFixCommonErrorsPreview = subNode.InnerText; + subNode = node.SelectSingleNode("MainToolsMergeShortLines"); + if (subNode != null) + settings.Shortcuts.MainToolsMergeShortLines = subNode.InnerText; + subNode = node.SelectSingleNode("MainToolsSplitLongLines"); + if (subNode != null) + settings.Shortcuts.MainToolsSplitLongLines = subNode.InnerText; + subNode = node.SelectSingleNode("MainToolsRenumber"); + if (subNode != null) + settings.Shortcuts.MainToolsRenumber = subNode.InnerText; + subNode = node.SelectSingleNode("MainToolsRemoveTextForHI"); + if (subNode != null) + settings.Shortcuts.MainToolsRemoveTextForHI = subNode.InnerText; + subNode = node.SelectSingleNode("MainToolsChangeCasing"); + if (subNode != null) + settings.Shortcuts.MainToolsChangeCasing = subNode.InnerText; + subNode = node.SelectSingleNode("MainToolsAutoDuration"); + if (subNode != null) + settings.Shortcuts.MainToolsAutoDuration = subNode.InnerText; + subNode = node.SelectSingleNode("MainToolsBatchConvert"); + if (subNode != null) + settings.Shortcuts.MainToolsBatchConvert = subNode.InnerText; + subNode = node.SelectSingleNode("MainToolsBeamer"); + if (subNode != null) + settings.Shortcuts.MainToolsBeamer = subNode.InnerText; + subNode = node.SelectSingleNode("MainToolsToggleTranslationOriginalInPreviews"); + if (subNode != null) + settings.Shortcuts.MainEditToggleTranslationOriginalInPreviews = subNode.InnerText; + subNode = node.SelectSingleNode("MainEditInverseSelection"); + if (subNode != null) + settings.Shortcuts.MainEditInverseSelection = subNode.InnerText; + subNode = node.SelectSingleNode("MainEditModifySelection"); + if (subNode != null) + settings.Shortcuts.MainEditModifySelection = subNode.InnerText; + subNode = node.SelectSingleNode("MainVideoPause"); + if (subNode != null) + settings.Shortcuts.MainVideoPause = subNode.InnerText; + subNode = node.SelectSingleNode("MainVideoPlayPauseToggle"); + if (subNode != null) + settings.Shortcuts.MainVideoPlayPauseToggle = subNode.InnerText; + subNode = node.SelectSingleNode("MainVideoShowHideVideo"); + if (subNode != null) + settings.Shortcuts.MainVideoShowHideVideo = subNode.InnerText; + subNode = node.SelectSingleNode("MainVideoToggleVideoControls"); + if (subNode != null) + settings.Shortcuts.MainVideoToggleVideoControls = subNode.InnerText; + subNode = node.SelectSingleNode("MainVideo1FrameLeft"); + if (subNode != null) + settings.Shortcuts.MainVideo1FrameLeft = subNode.InnerText; + subNode = node.SelectSingleNode("MainVideo1FrameRight"); + if (subNode != null) + settings.Shortcuts.MainVideo1FrameRight = subNode.InnerText; + subNode = node.SelectSingleNode("MainVideo100MsLeft"); + if (subNode != null) + settings.Shortcuts.MainVideo100MsLeft = subNode.InnerText; + subNode = node.SelectSingleNode("MainVideo100MsRight"); + if (subNode != null) + settings.Shortcuts.MainVideo100MsRight = subNode.InnerText; + subNode = node.SelectSingleNode("MainVideo500MsLeft"); + if (subNode != null) + settings.Shortcuts.MainVideo500MsLeft = subNode.InnerText; + subNode = node.SelectSingleNode("MainVideo500MsRight"); + if (subNode != null) + settings.Shortcuts.MainVideo500MsRight = subNode.InnerText; + subNode = node.SelectSingleNode("MainVideo1000MsLeft"); + if (subNode != null) + settings.Shortcuts.MainVideo1000MsLeft = subNode.InnerText; + subNode = node.SelectSingleNode("MainVideo1000MsRight"); + if (subNode != null) + settings.Shortcuts.MainVideo1000MsRight = subNode.InnerText; + subNode = node.SelectSingleNode("MainVideoFullscreen"); + if (subNode != null) + settings.Shortcuts.MainVideoFullscreen = subNode.InnerText; + subNode = node.SelectSingleNode("MainSpellCheck"); + if (subNode != null) + settings.Shortcuts.MainSpellCheck = subNode.InnerText; + subNode = node.SelectSingleNode("MainSpellCheckFindDoubleWords"); + if (subNode != null) + settings.Shortcuts.MainSpellCheckFindDoubleWords = subNode.InnerText; + subNode = node.SelectSingleNode("MainSpellCheckAddWordToNames"); + if (subNode != null) + settings.Shortcuts.MainSpellCheckAddWordToNames = subNode.InnerText; + subNode = node.SelectSingleNode("MainSynchronizationAdjustTimes"); + if (subNode != null) + settings.Shortcuts.MainSynchronizationAdjustTimes = subNode.InnerText; + subNode = node.SelectSingleNode("MainSynchronizationVisualSync"); + if (subNode != null) + settings.Shortcuts.MainSynchronizationVisualSync = subNode.InnerText; + subNode = node.SelectSingleNode("MainSynchronizationPointSync"); + if (subNode != null) + settings.Shortcuts.MainSynchronizationPointSync = subNode.InnerText; + subNode = node.SelectSingleNode("MainSynchronizationChangeFrameRate"); + if (subNode != null) + settings.Shortcuts.MainSynchronizationChangeFrameRate = subNode.InnerText; + subNode = node.SelectSingleNode("MainListViewItalic"); + if (subNode != null) + settings.Shortcuts.MainListViewItalic = subNode.InnerText; + subNode = node.SelectSingleNode("MainListViewToggleDashes"); + if (subNode != null) + settings.Shortcuts.MainListViewToggleDashes = subNode.InnerText; + subNode = node.SelectSingleNode("MainListViewAlignment"); + if (subNode != null) + settings.Shortcuts.MainListViewAlignment = subNode.InnerText; + subNode = node.SelectSingleNode("MainListViewCopyText"); + if (subNode != null) + settings.Shortcuts.MainListViewCopyText = subNode.InnerText; + subNode = node.SelectSingleNode("MainListViewCopyTextFromOriginalToCurrent"); + if (subNode != null) + settings.Shortcuts.MainListViewCopyTextFromOriginalToCurrent = subNode.InnerText; + subNode = node.SelectSingleNode("MainListViewAutoDuration"); + if (subNode != null) + settings.Shortcuts.MainListViewAutoDuration = subNode.InnerText; + subNode = node.SelectSingleNode("MainListViewColumnDeleteText"); + if (subNode != null) + settings.Shortcuts.MainListViewColumnDeleteText = subNode.InnerText; + subNode = node.SelectSingleNode("MainListViewColumnInsertText"); + if (subNode != null) + settings.Shortcuts.MainListViewColumnInsertText = subNode.InnerText; + subNode = node.SelectSingleNode("MainListViewColumnPaste"); + if (subNode != null) + settings.Shortcuts.MainListViewColumnPaste = subNode.InnerText; + subNode = node.SelectSingleNode("MainListViewFocusWaveform"); + if (subNode != null) + settings.Shortcuts.MainListViewFocusWaveform = subNode.InnerText; + subNode = node.SelectSingleNode("MainListViewGoToNextError"); + if (subNode != null) + settings.Shortcuts.MainListViewGoToNextError = subNode.InnerText; + subNode = node.SelectSingleNode("MainEditReverseStartAndEndingForRTL"); + if (subNode != null) + settings.Shortcuts.MainEditReverseStartAndEndingForRTL = subNode.InnerText; + subNode = node.SelectSingleNode("MainTextBoxItalic"); + if (subNode != null) + settings.Shortcuts.MainTextBoxItalic = subNode.InnerText; + subNode = node.SelectSingleNode("MainTextBoxSplitAtCursor"); + if (subNode != null) + settings.Shortcuts.MainTextBoxSplitAtCursor = subNode.InnerText; + subNode = node.SelectSingleNode("MainTextBoxMoveLastWordDown"); + if (subNode != null) + settings.Shortcuts.MainTextBoxMoveLastWordDown = subNode.InnerText; + subNode = node.SelectSingleNode("MainTextBoxMoveFirstWordFromNextUp"); + if (subNode != null) + settings.Shortcuts.MainTextBoxMoveFirstWordFromNextUp = subNode.InnerText; + subNode = node.SelectSingleNode("MainTextBoxSelectionToLower"); + if (subNode != null) + settings.Shortcuts.MainTextBoxSelectionToLower = subNode.InnerText; + subNode = node.SelectSingleNode("MainTextBoxSelectionToUpper"); + if (subNode != null) + settings.Shortcuts.MainTextBoxSelectionToUpper = subNode.InnerText; + subNode = node.SelectSingleNode("MainTextBoxToggleAutoDuration"); + if (subNode != null) + settings.Shortcuts.MainTextBoxToggleAutoDuration = subNode.InnerText; + subNode = node.SelectSingleNode("MainCreateInsertSubAtVideoPos"); + if (subNode != null) + settings.Shortcuts.MainCreateInsertSubAtVideoPos = subNode.InnerText; + subNode = node.SelectSingleNode("MainCreatePlayFromJustBefore"); + if (subNode != null) + settings.Shortcuts.MainCreatePlayFromJustBefore = subNode.InnerText; + subNode = node.SelectSingleNode("MainCreateSetStart"); + if (subNode != null) + settings.Shortcuts.MainCreateSetStart = subNode.InnerText; + subNode = node.SelectSingleNode("MainCreateSetEnd"); + if (subNode != null) + settings.Shortcuts.MainCreateSetEnd = subNode.InnerText; + subNode = node.SelectSingleNode("MainCreateSetEndAddNewAndGoToNew"); + if (subNode != null) + settings.Shortcuts.MainCreateSetEndAddNewAndGoToNew = subNode.InnerText; + subNode = node.SelectSingleNode("MainCreateStartDownEndUp"); + if (subNode != null) + settings.Shortcuts.MainCreateStartDownEndUp = subNode.InnerText; + subNode = node.SelectSingleNode("MainAdjustSetStartAndOffsetTheRest"); + if (subNode != null) + settings.Shortcuts.MainAdjustSetStartAndOffsetTheRest = subNode.InnerText; + subNode = node.SelectSingleNode("MainAdjustSetEndAndOffsetTheRest"); + if (subNode != null) + settings.Shortcuts.MainAdjustSetEndAndOffsetTheRest = subNode.InnerText; + subNode = node.SelectSingleNode("MainAdjustSetEndAndOffsetTheRestAndGoToNext"); + if (subNode != null) + settings.Shortcuts.MainAdjustSetEndAndOffsetTheRestAndGoToNext = subNode.InnerText; + subNode = node.SelectSingleNode("MainAdjustSetEndAndGotoNext"); + if (subNode != null) + settings.Shortcuts.MainAdjustSetEndAndGotoNext = subNode.InnerText; + subNode = node.SelectSingleNode("MainAdjustViaEndAutoStartAndGoToNext"); + if (subNode != null) + settings.Shortcuts.MainAdjustViaEndAutoStartAndGoToNext = subNode.InnerText; + subNode = node.SelectSingleNode("MainAdjustSetStartAutoDurationAndGoToNext"); + if (subNode != null) + settings.Shortcuts.MainAdjustSetStartAutoDurationAndGoToNext = subNode.InnerText; + subNode = node.SelectSingleNode("MainAdjustSetEndNextStartAndGoToNext"); + if (subNode != null) + settings.Shortcuts.MainAdjustSetEndNextStartAndGoToNext = subNode.InnerText; + subNode = node.SelectSingleNode("MainAdjustStartDownEndUpAndGoToNext"); + if (subNode != null) + settings.Shortcuts.MainAdjustStartDownEndUpAndGoToNext = subNode.InnerText; + subNode = node.SelectSingleNode("MainAdjustSetStart"); + if (subNode != null) + settings.Shortcuts.MainAdjustSetStart = subNode.InnerText; + subNode = node.SelectSingleNode("MainAdjustSetStartKeepDuration"); + if (subNode != null) + settings.Shortcuts.MainAdjustSetStartKeepDuration = subNode.InnerText; + subNode = node.SelectSingleNode("MainAdjustSetEnd"); + if (subNode != null) + settings.Shortcuts.MainAdjustSetEnd = subNode.InnerText; + subNode = node.SelectSingleNode("MainAdjustSelected100MsForward"); + if (subNode != null) + settings.Shortcuts.MainAdjustSelected100MsForward = subNode.InnerText; + subNode = node.SelectSingleNode("MainAdjustSelected100MsBack"); + if (subNode != null) + settings.Shortcuts.MainAdjustSelected100MsBack = subNode.InnerText; + subNode = node.SelectSingleNode("MainInsertAfter"); + if (subNode != null) + settings.Shortcuts.MainInsertAfter = subNode.InnerText; + subNode = node.SelectSingleNode("MainTextBoxInsertAfter"); + if (subNode != null) + settings.Shortcuts.MainTextBoxInsertAfter = subNode.InnerText; + subNode = node.SelectSingleNode("MainTextBoxAutoBreak"); + if (subNode != null) + settings.Shortcuts.MainTextBoxAutoBreak = subNode.InnerText; + subNode = node.SelectSingleNode("MainTextBoxUnbreak"); + if (subNode != null) + settings.Shortcuts.MainTextBoxUnbreak = subNode.InnerText; + subNode = node.SelectSingleNode("MainWaveformInsertAtCurrentPosition"); + if (subNode != null) + settings.Shortcuts.MainWaveformInsertAtCurrentPosition = subNode.InnerText; + subNode = node.SelectSingleNode("MainInsertBefore"); + if (subNode != null) + settings.Shortcuts.MainInsertBefore = subNode.InnerText; + subNode = node.SelectSingleNode("MainMergeDialog"); + if (subNode != null) + settings.Shortcuts.MainMergeDialog = subNode.InnerText; + subNode = node.SelectSingleNode("MainToggleFocus"); + if (subNode != null) + settings.Shortcuts.MainToggleFocus = subNode.InnerText; + subNode = node.SelectSingleNode("WaveformVerticalZoom"); + if (subNode != null) + settings.Shortcuts.WaveformVerticalZoom = subNode.InnerText; + subNode = node.SelectSingleNode("WaveformVerticalZoomOut"); + if (subNode != null) + settings.Shortcuts.WaveformVerticalZoomOut = subNode.InnerText; + subNode = node.SelectSingleNode("WaveformZoomIn"); + if (subNode != null) + settings.Shortcuts.WaveformZoomIn = subNode.InnerText; + subNode = node.SelectSingleNode("WaveformZoomOut"); + if (subNode != null) + settings.Shortcuts.WaveformZoomOut = subNode.InnerText; + subNode = node.SelectSingleNode("WaveformPlaySelection"); + if (subNode != null) + settings.Shortcuts.WaveformPlaySelection = subNode.InnerText; + subNode = node.SelectSingleNode("WaveformSearchSilenceForward"); + if (subNode != null) + settings.Shortcuts.WaveformSearchSilenceForward = subNode.InnerText; + subNode = node.SelectSingleNode("WaveformSearchSilenceBack"); + if (subNode != null) + settings.Shortcuts.WaveformSearchSilenceBack = subNode.InnerText; + subNode = node.SelectSingleNode("WaveformAddTextHere"); + if (subNode != null) + settings.Shortcuts.WaveformAddTextHere = subNode.InnerText; + subNode = node.SelectSingleNode("WaveformFocusListView"); + if (subNode != null) + settings.Shortcuts.WaveformFocusListView = subNode.InnerText; + subNode = node.SelectSingleNode("MainTranslateCustomSearch1"); + if (subNode != null) + settings.Shortcuts.MainTranslateCustomSearch1 = subNode.InnerText; + subNode = node.SelectSingleNode("MainTranslateCustomSearch2"); + if (subNode != null) + settings.Shortcuts.MainTranslateCustomSearch2 = subNode.InnerText; + subNode = node.SelectSingleNode("MainTranslateCustomSearch3"); + if (subNode != null) + settings.Shortcuts.MainTranslateCustomSearch3 = subNode.InnerText; + subNode = node.SelectSingleNode("MainTranslateCustomSearch4"); + if (subNode != null) + settings.Shortcuts.MainTranslateCustomSearch4 = subNode.InnerText; + subNode = node.SelectSingleNode("MainTranslateCustomSearch5"); + if (subNode != null) + settings.Shortcuts.MainTranslateCustomSearch5 = subNode.InnerText; + subNode = node.SelectSingleNode("MainTranslateCustomSearch6"); + if (subNode != null) + settings.Shortcuts.MainTranslateCustomSearch6 = subNode.InnerText; + } + + settings.RemoveTextForHearingImpaired = new RemoveTextForHearingImpairedSettings(); + node = doc.DocumentElement.SelectSingleNode("RemoveTextForHearingImpaired"); + if (node != null) + { + subNode = node.SelectSingleNode("RemoveTextBetweenBrackets"); + if (subNode != null) + settings.RemoveTextForHearingImpaired.RemoveTextBetweenBrackets = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("RemoveTextBetweenParentheses"); + if (subNode != null) + settings.RemoveTextForHearingImpaired.RemoveTextBetweenParentheses = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("RemoveTextBetweenCurlyBrackets"); + if (subNode != null) + settings.RemoveTextForHearingImpaired.RemoveTextBetweenCurlyBrackets = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("RemoveTextBetweenQuestionMarks"); + if (subNode != null) + settings.RemoveTextForHearingImpaired.RemoveTextBetweenQuestionMarks = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("RemoveTextBetweenCustom"); + if (subNode != null) + settings.RemoveTextForHearingImpaired.RemoveTextBetweenCustom = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("RemoveTextBetweenCustomBefore"); + if (subNode != null) + settings.RemoveTextForHearingImpaired.RemoveTextBetweenCustomBefore = subNode.InnerText; + subNode = node.SelectSingleNode("RemoveTextBetweenCustomAfter"); + if (subNode != null) + settings.RemoveTextForHearingImpaired.RemoveTextBetweenCustomAfter = subNode.InnerText; + subNode = node.SelectSingleNode("RemoveTextBetweenOnlySeperateLines"); + if (subNode != null) + settings.RemoveTextForHearingImpaired.RemoveTextBetweenOnlySeperateLines = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("RemoveTextBeforeColon"); + if (subNode != null) + settings.RemoveTextForHearingImpaired.RemoveTextBeforeColon = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("RemoveTextBeforeColonOnlyIfUppercase"); + if (subNode != null) + settings.RemoveTextForHearingImpaired.RemoveTextBeforeColonOnlyIfUppercase = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("RemoveTextBeforeColonOnlyOnSeparateLine"); + if (subNode != null) + settings.RemoveTextForHearingImpaired.RemoveTextBeforeColonOnlyOnSeparateLine = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("RemoveIfAllUppercase"); + if (subNode != null) + settings.RemoveTextForHearingImpaired.RemoveIfAllUppercase = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("RemoveInterjections"); + if (subNode != null) + settings.RemoveTextForHearingImpaired.RemoveInterjections = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("RemoveIfContains"); + if (subNode != null) + settings.RemoveTextForHearingImpaired.RemoveIfContains = Convert.ToBoolean(subNode.InnerText); + subNode = node.SelectSingleNode("RemoveIfContainsText"); + if (subNode != null) + settings.RemoveTextForHearingImpaired.RemoveIfContainsText = subNode.InnerText; + } + + settings.SubtitleBeaming = new SubtitleBeaming(); + node = doc.DocumentElement.SelectSingleNode("SubtitleBeaming"); + if (node != null) + { + subNode = node.SelectSingleNode("FontName"); + if (subNode != null) + settings.SubtitleBeaming.FontName = subNode.InnerText; + subNode = node.SelectSingleNode("FontColor"); + if (subNode != null) + settings.SubtitleBeaming.FontColor = Color.FromArgb(Convert.ToInt32(subNode.InnerText)); + subNode = node.SelectSingleNode("FontSize"); + if (subNode != null) + settings.SubtitleBeaming.FontSize = Convert.ToInt32(subNode.InnerText); + subNode = node.SelectSingleNode("BorderColor"); + if (subNode != null) + settings.SubtitleBeaming.BorderColor = Color.FromArgb(Convert.ToInt32(subNode.InnerText)); + subNode = node.SelectSingleNode("BorderWidth"); + if (subNode != null) + settings.SubtitleBeaming.BorderWidth = Convert.ToInt32(subNode.InnerText); + } + + return settings; + } + + private static void CustomSerialize(string fileName, Settings settings) + { + var xws = new XmlWriterSettings { Indent = true }; + var sb = new StringBuilder(); + using (var textWriter = XmlWriter.Create(sb, xws)) + { + textWriter.WriteStartDocument(); + + textWriter.WriteStartElement("Settings", ""); + + textWriter.WriteStartElement("RecentFiles", ""); + textWriter.WriteStartElement("FileNames", ""); + foreach (var item in settings.RecentFiles.Files) + { + textWriter.WriteStartElement("FileName"); + if (item.OriginalFileName != null) + textWriter.WriteAttributeString("OriginalFileName", item.OriginalFileName); + if (item.VideoFileName != null) + textWriter.WriteAttributeString("VideoFileName", item.VideoFileName); + textWriter.WriteAttributeString("FirstVisibleIndex", item.FirstVisibleIndex.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteAttributeString("FirstSelectedIndex", item.FirstSelectedIndex.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteString(item.FileName); + textWriter.WriteEndElement(); + } + textWriter.WriteEndElement(); + textWriter.WriteEndElement(); + + textWriter.WriteStartElement("General", ""); + textWriter.WriteElementString("ShowToolbarNew", settings.General.ShowToolbarNew.ToString()); + textWriter.WriteElementString("ShowToolbarOpen", settings.General.ShowToolbarOpen.ToString()); + textWriter.WriteElementString("ShowToolbarSave", settings.General.ShowToolbarSave.ToString()); + textWriter.WriteElementString("ShowToolbarSaveAs", settings.General.ShowToolbarSaveAs.ToString()); + textWriter.WriteElementString("ShowToolbarFind", settings.General.ShowToolbarFind.ToString()); + textWriter.WriteElementString("ShowToolbarReplace", settings.General.ShowToolbarReplace.ToString()); + textWriter.WriteElementString("ShowToolbarFixCommonErrors", settings.General.ShowToolbarFixCommonErrors.ToString()); + textWriter.WriteElementString("ShowToolbarVisualSync", settings.General.ShowToolbarVisualSync.ToString()); + textWriter.WriteElementString("ShowToolbarSpellCheck", settings.General.ShowToolbarSpellCheck.ToString()); + textWriter.WriteElementString("ShowToolbarSettings", settings.General.ShowToolbarSettings.ToString()); + textWriter.WriteElementString("ShowToolbarHelp", settings.General.ShowToolbarHelp.ToString()); + textWriter.WriteElementString("ShowFrameRate", settings.General.ShowFrameRate.ToString()); + textWriter.WriteElementString("ShowVideoPlayer", settings.General.ShowVideoPlayer.ToString()); + textWriter.WriteElementString("ShowAudioVisualizer", settings.General.ShowAudioVisualizer.ToString()); + textWriter.WriteElementString("ShowWaveform", settings.General.ShowWaveform.ToString()); + textWriter.WriteElementString("ShowSpectrogram", settings.General.ShowSpectrogram.ToString()); + textWriter.WriteElementString("DefaultFrameRate", settings.General.DefaultFrameRate.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("DefaultSubtitleFormat", settings.General.DefaultSubtitleFormat); + textWriter.WriteElementString("DefaultEncoding", settings.General.DefaultEncoding); + textWriter.WriteElementString("AutoConvertToUtf8", settings.General.AutoConvertToUtf8.ToString()); + textWriter.WriteElementString("AutoGuessAnsiEncoding", settings.General.AutoGuessAnsiEncoding.ToString()); + textWriter.WriteElementString("_subtitleFontName", settings.General.SubtitleFontName); + textWriter.WriteElementString("SubtitleFontSize", settings.General.SubtitleFontSize.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("SubtitleFontBold", settings.General.SubtitleFontBold.ToString()); + textWriter.WriteElementString("SubtitleFontColor", settings.General.SubtitleFontColor.ToArgb().ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("SubtitleBackgroundColor", settings.General.SubtitleBackgroundColor.ToArgb().ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("CenterSubtitleInTextBox", settings.General.CenterSubtitleInTextBox.ToString()); + textWriter.WriteElementString("ShowRecentFiles", settings.General.ShowRecentFiles.ToString()); + textWriter.WriteElementString("RememberSelectedLine", settings.General.RememberSelectedLine.ToString()); + textWriter.WriteElementString("StartLoadLastFile", settings.General.StartLoadLastFile.ToString()); + textWriter.WriteElementString("StartRememberPositionAndSize", settings.General.StartRememberPositionAndSize.ToString()); + textWriter.WriteElementString("StartPosition", settings.General.StartPosition); + textWriter.WriteElementString("StartSize", settings.General.StartSize); + textWriter.WriteElementString("SplitContainerMainSplitterDistance", settings.General.SplitContainerMainSplitterDistance.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("SplitContainer1SplitterDistance", settings.General.SplitContainer1SplitterDistance.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("SplitContainerListViewAndTextSplitterDistance", settings.General.SplitContainerListViewAndTextSplitterDistance.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("StartInSourceView", settings.General.StartInSourceView.ToString()); + textWriter.WriteElementString("RemoveBlankLinesWhenOpening", settings.General.RemoveBlankLinesWhenOpening.ToString()); + textWriter.WriteElementString("SubtitleLineMaximumLength", settings.General.SubtitleLineMaximumLength.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("SubtitleMinimumDisplayMilliseconds", settings.General.SubtitleMinimumDisplayMilliseconds.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("SubtitleMaximumDisplayMilliseconds", settings.General.SubtitleMaximumDisplayMilliseconds.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("MinimumMillisecondsBetweenLines", settings.General.MinimumMillisecondsBetweenLines.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("SetStartEndHumanDelay", settings.General.SetStartEndHumanDelay.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("AutoWrapLineWhileTyping", settings.General.AutoWrapLineWhileTyping.ToString()); + textWriter.WriteElementString("SubtitleMaximumCharactersPerSeconds", settings.General.SubtitleMaximumCharactersPerSeconds.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("SubtitleOptimalCharactersPerSeconds", settings.General.SubtitleOptimalCharactersPerSeconds.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("SpellCheckLanguage", settings.General.SpellCheckLanguage); + textWriter.WriteElementString("VideoPlayer", settings.General.VideoPlayer); + textWriter.WriteElementString("VideoPlayerDefaultVolume", settings.General.VideoPlayerDefaultVolume.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("VideoPlayerPreviewFontSize", settings.General.VideoPlayerPreviewFontSize.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("VideoPlayerPreviewFontBold", settings.General.VideoPlayerPreviewFontBold.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("VideoPlayerShowStopButton", settings.General.VideoPlayerShowStopButton.ToString()); + textWriter.WriteElementString("VideoPlayerShowStopMute", settings.General.VideoPlayerShowMuteButton.ToString()); + textWriter.WriteElementString("VideoPlayerShowStopFullscreen", settings.General.VideoPlayerShowFullscreenButton.ToString()); + textWriter.WriteElementString("Language", settings.General.Language); + textWriter.WriteElementString("ListViewLineSeparatorString", settings.General.ListViewLineSeparatorString); + textWriter.WriteElementString("ListViewDoubleClickAction", settings.General.ListViewDoubleClickAction.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("UppercaseLetters", settings.General.UppercaseLetters); + textWriter.WriteElementString("DefaultAdjustMilliseconds", settings.General.DefaultAdjustMilliseconds.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("AutoRepeatOn", settings.General.AutoRepeatOn.ToString()); + textWriter.WriteElementString("AutoRepeatCount", settings.General.AutoRepeatCount.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("AutoContinueOn", settings.General.AutoContinueOn.ToString()); + textWriter.WriteElementString("SyncListViewWithVideoWhilePlaying", settings.General.SyncListViewWithVideoWhilePlaying.ToString()); + textWriter.WriteElementString("AutoBackupSeconds", settings.General.AutoBackupSeconds.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("SpellChecker", settings.General.SpellChecker); + textWriter.WriteElementString("AllowEditOfOriginalSubtitle", settings.General.AllowEditOfOriginalSubtitle.ToString()); + textWriter.WriteElementString("PromptDeleteLines", settings.General.PromptDeleteLines.ToString()); + textWriter.WriteElementString("Undocked", settings.General.Undocked.ToString()); + textWriter.WriteElementString("UndockedVideoPosition", settings.General.UndockedVideoPosition); + textWriter.WriteElementString("UndockedWaveformPosition", settings.General.UndockedWaveformPosition); + textWriter.WriteElementString("UndockedVideoControlsPosition", settings.General.UndockedVideoControlsPosition); + textWriter.WriteElementString("WaveformCenter", settings.General.WaveformCenter.ToString()); + textWriter.WriteElementString("SmallDelayMilliseconds", settings.General.SmallDelayMilliseconds.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("LargeDelayMilliseconds", settings.General.LargeDelayMilliseconds.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("ShowOriginalAsPreviewIfAvailable", settings.General.ShowOriginalAsPreviewIfAvailable.ToString()); + textWriter.WriteElementString("LastPacCodePage", settings.General.LastPacCodePage.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("OpenSubtitleExtraExtensions", settings.General.OpenSubtitleExtraExtensions); + textWriter.WriteElementString("ListViewColumnsRememberSize", settings.General.ListViewColumnsRememberSize.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("ListViewNumberWidth", settings.General.ListViewNumberWidth.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("ListViewStartWidth", settings.General.ListViewStartWidth.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("ListViewEndWidth", settings.General.ListViewEndWidth.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("ListViewDurationWidth", settings.General.ListViewDurationWidth.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("ListViewTextWidth", settings.General.ListViewTextWidth.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("VlcWaveTranscodeSettings", settings.General.VlcWaveTranscodeSettings); + textWriter.WriteElementString("VlcLocation", settings.General.VlcLocation); + textWriter.WriteElementString("VlcLocationRelative", settings.General.VlcLocationRelative); + textWriter.WriteElementString("MpcHcLocation", settings.General.MpcHcLocation); + textWriter.WriteElementString("UseFFmpegForWaveExtraction", settings.General.UseFFmpegForWaveExtraction.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("FFmpegLocation", settings.General.FFmpegLocation); + textWriter.WriteElementString("UseTimeFormatHHMMSSFF", settings.General.UseTimeFormatHHMMSSFF.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("ClearStatusBarAfterSeconds", settings.General.ClearStatusBarAfterSeconds.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("Company", settings.General.Company); + textWriter.WriteElementString("MoveVideo100Or500MsPlaySmallSample", settings.General.MoveVideo100Or500MsPlaySmallSample.ToString()); + textWriter.WriteElementString("DisableVideoAutoLoading", settings.General.DisableVideoAutoLoading.ToString()); + textWriter.WriteElementString("RightToLeftMode", settings.General.RightToLeftMode.ToString()); + textWriter.WriteElementString("LastSaveAsFormat", settings.General.LastSaveAsFormat); + textWriter.WriteElementString("CheckForUpdates", settings.General.CheckForUpdates.ToString()); + textWriter.WriteElementString("LastCheckForUpdates", settings.General.LastCheckForUpdates.ToString("yyyy-MM-dd")); + textWriter.WriteElementString("ShowProgress", settings.General.ShowProgress.ToString()); + textWriter.WriteElementString("ShowBetaStuff", settings.General.ShowBetaStuff.ToString()); + textWriter.WriteElementString("NewEmptyDefaultMs", settings.General.NewEmptyDefaultMs.ToString(CultureInfo.InvariantCulture)); + + textWriter.WriteEndElement(); + + textWriter.WriteStartElement("Tools", ""); + textWriter.WriteElementString("StartSceneIndex", settings.Tools.StartSceneIndex.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("EndSceneIndex", settings.Tools.EndSceneIndex.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("VerifyPlaySeconds", settings.Tools.VerifyPlaySeconds.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("MergeLinesShorterThan", settings.Tools.MergeLinesShorterThan.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("FixShortDisplayTimesAllowMoveStartTime", settings.Tools.FixShortDisplayTimesAllowMoveStartTime.ToString()); + textWriter.WriteElementString("MusicSymbol", settings.Tools.MusicSymbol); + textWriter.WriteElementString("MusicSymbolToReplace", settings.Tools.MusicSymbolToReplace); + textWriter.WriteElementString("UnicodeSymbolsToInsert", settings.Tools.UnicodeSymbolsToInsert); + textWriter.WriteElementString("SpellCheckAutoChangeNames", settings.Tools.SpellCheckAutoChangeNames.ToString()); + textWriter.WriteElementString("SpellCheckOneLetterWords", settings.Tools.SpellCheckOneLetterWords.ToString()); + textWriter.WriteElementString("SpellCheckEnglishAllowInQuoteAsIng", settings.Tools.SpellCheckEnglishAllowInQuoteAsIng.ToString()); + textWriter.WriteElementString("SpellCheckShowCompletedMessage", settings.Tools.SpellCheckShowCompletedMessage.ToString()); + textWriter.WriteElementString("OcrFixUseHardcodedRules", settings.Tools.OcrFixUseHardcodedRules.ToString()); + textWriter.WriteElementString("Interjections", settings.Tools.Interjections); + textWriter.WriteElementString("MicrosoftBingApiId", settings.Tools.MicrosoftBingApiId); + textWriter.WriteElementString("GoogleApiKey", settings.Tools.GoogleApiKey); + textWriter.WriteElementString("UseGooleApiPaidService", settings.Tools.UseGooleApiPaidService.ToString()); + textWriter.WriteElementString("GoogleTranslateLastTargetLanguage", settings.Tools.GoogleTranslateLastTargetLanguage); + textWriter.WriteElementString("ListViewSyntaxColorDurationSmall", settings.Tools.ListViewSyntaxColorDurationSmall.ToString()); + textWriter.WriteElementString("ListViewSyntaxColorDurationBig", settings.Tools.ListViewSyntaxColorDurationBig.ToString()); + textWriter.WriteElementString("ListViewSyntaxColorLongLines", settings.Tools.ListViewSyntaxColorLongLines.ToString()); + textWriter.WriteElementString("ListViewSyntaxMoreThanXLines", settings.Tools.ListViewSyntaxMoreThanXLines.ToString()); + textWriter.WriteElementString("ListViewSyntaxMoreThanXLinesX", settings.Tools.ListViewSyntaxMoreThanXLinesX.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("ListViewSyntaxColorOverlap", settings.Tools.ListViewSyntaxColorOverlap.ToString()); + textWriter.WriteElementString("ListViewSyntaxErrorColor", settings.Tools.ListViewSyntaxErrorColor.ToArgb().ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("ListViewUnfocusedSelectedColor", settings.Tools.ListViewUnfocusedSelectedColor.ToArgb().ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("SplitAdvanced", settings.Tools.SplitAdvanced.ToString()); + textWriter.WriteElementString("SplitOutputFolder", settings.Tools.SplitOutputFolder); + textWriter.WriteElementString("SplitNumberOfParts", settings.Tools.SplitNumberOfParts.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("SplitVia", settings.Tools.SplitVia); + textWriter.WriteElementString("NewEmptyTranslationText", settings.Tools.NewEmptyTranslationText); + textWriter.WriteElementString("BatchConvertOutputFolder", settings.Tools.BatchConvertOutputFolder); + textWriter.WriteElementString("BatchConvertOverwriteExisting", settings.Tools.BatchConvertOverwriteExisting.ToString()); + textWriter.WriteElementString("BatchConvertOverwriteOriginal", settings.Tools.BatchConvertOverwriteOriginal.ToString()); + textWriter.WriteElementString("BatchConvertRemoveFormatting", settings.Tools.BatchConvertRemoveFormatting.ToString()); + textWriter.WriteElementString("BatchConvertFixCasing", settings.Tools.BatchConvertFixCasing.ToString()); + textWriter.WriteElementString("BatchConvertRemoveTextForHI", settings.Tools.BatchConvertRemoveTextForHI.ToString()); + textWriter.WriteElementString("BatchConvertSplitLongLines", settings.Tools.BatchConvertSplitLongLines.ToString()); + textWriter.WriteElementString("BatchConvertFixCommonErrors", settings.Tools.BatchConvertFixCommonErrors.ToString()); + textWriter.WriteElementString("BatchConvertMultipleReplace", settings.Tools.BatchConvertMultipleReplace.ToString()); + textWriter.WriteElementString("BatchConvertAutoBalance", settings.Tools.BatchConvertAutoBalance.ToString()); + textWriter.WriteElementString("BatchConvertSetMinDisplayTimeBetweenSubtitles", settings.Tools.BatchConvertSetMinDisplayTimeBetweenSubtitles.ToString()); + textWriter.WriteElementString("BatchConvertLanguage", settings.Tools.BatchConvertLanguage); + textWriter.WriteElementString("BatchConvertFormat", settings.Tools.BatchConvertFormat); + textWriter.WriteElementString("ModifySelectionRule", settings.Tools.ModifySelectionRule); + textWriter.WriteElementString("ModifySelectionText", settings.Tools.ModifySelectionText); + textWriter.WriteElementString("ModifySelectionCaseSensitive", settings.Tools.ModifySelectionCaseSensitive.ToString()); + textWriter.WriteElementString("ExportVobSubFontName", settings.Tools.ExportVobSubFontName); + textWriter.WriteElementString("ExportVobSubFontSize", settings.Tools.ExportVobSubFontSize.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("ExportVobSubVideoResolution", settings.Tools.ExportVobSubVideoResolution); + textWriter.WriteElementString("ExportVobSubLanguage", settings.Tools.ExportVobSubLanguage); + textWriter.WriteElementString("ExportVobSubSimpleRendering", settings.Tools.ExportVobSubSimpleRendering.ToString()); + textWriter.WriteElementString("ExportVobAntiAliasingWithTransparency", settings.Tools.ExportVobAntiAliasingWithTransparency.ToString()); + textWriter.WriteElementString("ExportBluRayFontName", settings.Tools.ExportBluRayFontName); + textWriter.WriteElementString("ExportBluRayFontSize", settings.Tools.ExportBluRayFontSize.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("ExportFcpFontName", settings.Tools.ExportFcpFontName); + textWriter.WriteElementString("ExportFontNameOther", settings.Tools.ExportFontNameOther); + textWriter.WriteElementString("ExportFcpFontSize", settings.Tools.ExportFcpFontSize.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("ExportFcpImageType", settings.Tools.ExportFcpImageType); + textWriter.WriteElementString("ExportBdnXmlImageType", settings.Tools.ExportBdnXmlImageType); + textWriter.WriteElementString("ExportLastFontSize", settings.Tools.ExportLastFontSize.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("ExportLastLineHeight", settings.Tools.ExportLastLineHeight.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("ExportLastBorderWidth", settings.Tools.ExportLastBorderWidth.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("ExportLastFontBold", settings.Tools.ExportLastFontBold.ToString()); + textWriter.WriteElementString("ExportBluRayVideoResolution", settings.Tools.ExportBluRayVideoResolution); + textWriter.WriteElementString("ExportFontColor", settings.Tools.ExportFontColor.ToArgb().ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("ExportBorderColor", settings.Tools.ExportBorderColor.ToArgb().ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("ExportShadowColor", settings.Tools.ExportShadowColor.ToArgb().ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("ExportBottomMargin", settings.Tools.ExportBottomMargin.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("ExportHorizontalAlignment", settings.Tools.ExportHorizontalAlignment.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("ExportBluRayBottomMargin", settings.Tools.ExportBluRayBottomMargin.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("ExportBluRayShadow", settings.Tools.ExportBluRayShadow.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("Export3DType", settings.Tools.Export3DType.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("Export3DDepth", settings.Tools.Export3DDepth.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("ExportLastShadowTransparency", settings.Tools.ExportLastShadowTransparency.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("ExportLastFrameRate", settings.Tools.ExportLastFrameRate.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("ExportPenLineJoin", settings.Tools.ExportPenLineJoin); + textWriter.WriteElementString("FixCommonErrorsFixOverlapAllowEqualEndStart", settings.Tools.FixCommonErrorsFixOverlapAllowEqualEndStart.ToString()); + textWriter.WriteElementString("ImportTextSplitting", settings.Tools.ImportTextSplitting); + textWriter.WriteElementString("ImportTextMergeShortLines", settings.Tools.ImportTextMergeShortLines.ToString()); + textWriter.WriteElementString("ImportTextLineBreak", settings.Tools.ImportTextLineBreak); + textWriter.WriteElementString("GenerateTimeCodePatterns", settings.Tools.GenerateTimeCodePatterns); + textWriter.WriteElementString("MusicSymbolStyle", settings.Tools.MusicSymbolStyle); + textWriter.WriteElementString("BridgeGapMilliseconds", settings.Tools.BridgeGapMilliseconds.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("ExportCustomTemplates", settings.Tools.ExportCustomTemplates); + textWriter.WriteElementString("ChangeCasingChoice", settings.Tools.ChangeCasingChoice); + textWriter.WriteElementString("UseNoLineBreakAfter", settings.Tools.UseNoLineBreakAfter.ToString()); + textWriter.WriteElementString("NoLineBreakAfterEnglish", settings.Tools.NoLineBreakAfterEnglish); + if (settings.Tools.FindHistory != null && settings.Tools.FindHistory.Count > 0) + { + const int maximumFindHistoryItems = 10; + textWriter.WriteStartElement("FindHistory", ""); + int maxIndex = settings.Tools.FindHistory.Count; + if (maxIndex > maximumFindHistoryItems) + maxIndex = maximumFindHistoryItems; + for (int index = 0; index < maxIndex; index++) + { + var text = settings.Tools.FindHistory[index]; + textWriter.WriteElementString("Text", text); + } + textWriter.WriteEndElement(); + } + textWriter.WriteEndElement(); + + textWriter.WriteStartElement("SubtitleSettings", ""); + textWriter.WriteElementString("SsaFontName", settings.SubtitleSettings.SsaFontName); + textWriter.WriteElementString("SsaFontSize", settings.SubtitleSettings.SsaFontSize.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("SsaFontColorArgb", settings.SubtitleSettings.SsaFontColorArgb.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("SsaOutline", settings.SubtitleSettings.SsaOutline.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("SsaShadow", settings.SubtitleSettings.SsaShadow.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("SsaOpaqueBox", settings.SubtitleSettings.SsaOpaqueBox.ToString()); + textWriter.WriteElementString("DCinemaFontFile", settings.SubtitleSettings.DCinemaFontFile); + textWriter.WriteElementString("DCinemaFontSize", settings.SubtitleSettings.DCinemaFontSize.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("DCinemaBottomMargin", settings.SubtitleSettings.DCinemaBottomMargin.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("DCinemaZPosition", settings.SubtitleSettings.DCinemaZPosition.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("DCinemaFadeUpTime", settings.SubtitleSettings.DCinemaFadeUpTime.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("DCinemaFadeDownTime", settings.SubtitleSettings.DCinemaFadeDownTime.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("SamiDisplayTwoClassesAsTwoSubtitles", settings.SubtitleSettings.SamiDisplayTwoClassesAsTwoSubtitles.ToString()); + textWriter.WriteElementString("SamiFullHtmlEncode", settings.SubtitleSettings.SamiHtmlEncodeMode.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("TimedText10TimeCodeFormat", settings.SubtitleSettings.TimedText10TimeCodeFormat); + textWriter.WriteElementString("FcpFontSize", settings.SubtitleSettings.FcpFontSize.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("FcpFontName", settings.SubtitleSettings.FcpFontName); + textWriter.WriteElementString("CheetahCaptionAlwayWriteEndTime", settings.SubtitleSettings.CheetahCaptionAlwayWriteEndTime.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("NuendoCharacterListFile", settings.SubtitleSettings.NuendoCharacterListFile); + textWriter.WriteEndElement(); + + textWriter.WriteStartElement("Proxy", ""); + textWriter.WriteElementString("ProxyAddress", settings.Proxy.ProxyAddress); + textWriter.WriteElementString("UserName", settings.Proxy.UserName); + textWriter.WriteElementString("Password", settings.Proxy.Password); + textWriter.WriteElementString("Domain", settings.Proxy.Domain); + textWriter.WriteEndElement(); + + textWriter.WriteStartElement("WordLists", ""); + textWriter.WriteElementString("LastLanguage", settings.WordLists.LastLanguage); + textWriter.WriteElementString("NamesEtcUrl", settings.WordLists.NamesEtcUrl); + textWriter.WriteElementString("UseOnlineNamesEtc", settings.WordLists.UseOnlineNamesEtc.ToString()); + textWriter.WriteEndElement(); + + textWriter.WriteStartElement("CommonErrors", ""); + textWriter.WriteElementString("StartPosition", settings.CommonErrors.StartPosition); + textWriter.WriteElementString("StartSize", settings.CommonErrors.StartSize); + textWriter.WriteElementString("EmptyLinesTicked", settings.CommonErrors.EmptyLinesTicked.ToString()); + textWriter.WriteElementString("OverlappingDisplayTimeTicked", settings.CommonErrors.OverlappingDisplayTimeTicked.ToString()); + textWriter.WriteElementString("TooShortDisplayTimeTicked", settings.CommonErrors.TooShortDisplayTimeTicked.ToString()); + textWriter.WriteElementString("TooLongDisplayTimeTicked", settings.CommonErrors.TooLongDisplayTimeTicked.ToString()); + textWriter.WriteElementString("InvalidItalicTagsTicked", settings.CommonErrors.InvalidItalicTagsTicked.ToString()); + textWriter.WriteElementString("BreakLongLinesTicked", settings.CommonErrors.BreakLongLinesTicked.ToString()); + textWriter.WriteElementString("MergeShortLinesTicked", settings.CommonErrors.MergeShortLinesTicked.ToString()); + textWriter.WriteElementString("MergeShortLinesAllTicked", settings.CommonErrors.MergeShortLinesAllTicked.ToString()); + textWriter.WriteElementString("UnneededSpacesTicked", settings.CommonErrors.UnneededSpacesTicked.ToString()); + textWriter.WriteElementString("UnneededPeriodsTicked", settings.CommonErrors.UnneededPeriodsTicked.ToString()); + textWriter.WriteElementString("MissingSpacesTicked", settings.CommonErrors.MissingSpacesTicked.ToString()); + textWriter.WriteElementString("AddMissingQuotesTicked", settings.CommonErrors.AddMissingQuotesTicked.ToString()); + textWriter.WriteElementString("Fix3PlusLinesTicked", settings.CommonErrors.Fix3PlusLinesTicked.ToString()); + textWriter.WriteElementString("FixHyphensTicked", settings.CommonErrors.FixHyphensTicked.ToString()); + textWriter.WriteElementString("FixHyphensAddTicked", settings.CommonErrors.FixHyphensAddTicked.ToString()); + textWriter.WriteElementString("UppercaseIInsideLowercaseWordTicked", settings.CommonErrors.UppercaseIInsideLowercaseWordTicked.ToString()); + textWriter.WriteElementString("DoubleApostropheToQuoteTicked", settings.CommonErrors.DoubleApostropheToQuoteTicked.ToString()); + textWriter.WriteElementString("AddPeriodAfterParagraphTicked", settings.CommonErrors.AddPeriodAfterParagraphTicked.ToString()); + textWriter.WriteElementString("StartWithUppercaseLetterAfterParagraphTicked", settings.CommonErrors.StartWithUppercaseLetterAfterParagraphTicked.ToString()); + textWriter.WriteElementString("StartWithUppercaseLetterAfterPeriodInsideParagraphTicked", settings.CommonErrors.StartWithUppercaseLetterAfterPeriodInsideParagraphTicked.ToString()); + textWriter.WriteElementString("StartWithUppercaseLetterAfterColonTicked", settings.CommonErrors.StartWithUppercaseLetterAfterColonTicked.ToString()); + textWriter.WriteElementString("AloneLowercaseIToUppercaseIEnglishTicked", settings.CommonErrors.AloneLowercaseIToUppercaseIEnglishTicked.ToString()); + textWriter.WriteElementString("FixOcrErrorsViaReplaceListTicked", settings.CommonErrors.FixOcrErrorsViaReplaceListTicked.ToString()); + textWriter.WriteElementString("RemoveSpaceBetweenNumberTicked", settings.CommonErrors.RemoveSpaceBetweenNumberTicked.ToString()); + textWriter.WriteElementString("FixDialogsOnOneLineTicked", settings.CommonErrors.FixDialogsOnOneLineTicked.ToString()); + textWriter.WriteElementString("TurkishAnsiTicked", settings.CommonErrors.TurkishAnsiTicked.ToString()); + textWriter.WriteElementString("DanishLetterITicked", settings.CommonErrors.DanishLetterITicked.ToString()); + textWriter.WriteElementString("SpanishInvertedQuestionAndExclamationMarksTicked", settings.CommonErrors.SpanishInvertedQuestionAndExclamationMarksTicked.ToString()); + textWriter.WriteElementString("FixDoubleDashTicked", settings.CommonErrors.FixDoubleDashTicked.ToString()); + textWriter.WriteElementString("FixDoubleGreaterThanTicked", settings.CommonErrors.FixDoubleGreaterThanTicked.ToString()); + textWriter.WriteElementString("FixEllipsesStartTicked", settings.CommonErrors.FixEllipsesStartTicked.ToString()); + textWriter.WriteElementString("FixMissingOpenBracketTicked", settings.CommonErrors.FixMissingOpenBracketTicked.ToString()); + textWriter.WriteElementString("FixMusicNotationTicked", settings.CommonErrors.FixMusicNotationTicked.ToString()); + textWriter.WriteEndElement(); + + textWriter.WriteStartElement("VideoControls", ""); + textWriter.WriteElementString("CustomSearchText1", settings.VideoControls.CustomSearchText1); + textWriter.WriteElementString("CustomSearchText2", settings.VideoControls.CustomSearchText2); + textWriter.WriteElementString("CustomSearchText3", settings.VideoControls.CustomSearchText3); + textWriter.WriteElementString("CustomSearchText4", settings.VideoControls.CustomSearchText4); + textWriter.WriteElementString("CustomSearchText5", settings.VideoControls.CustomSearchText5); + textWriter.WriteElementString("CustomSearchText6", settings.VideoControls.CustomSearchText6); + textWriter.WriteElementString("CustomSearchUrl1", settings.VideoControls.CustomSearchUrl1); + textWriter.WriteElementString("CustomSearchUrl2", settings.VideoControls.CustomSearchUrl2); + textWriter.WriteElementString("CustomSearchUrl3", settings.VideoControls.CustomSearchUrl3); + textWriter.WriteElementString("CustomSearchUrl4", settings.VideoControls.CustomSearchUrl4); + textWriter.WriteElementString("CustomSearchUrl5", settings.VideoControls.CustomSearchUrl5); + textWriter.WriteElementString("CustomSearchUrl6", settings.VideoControls.CustomSearchUrl6); + textWriter.WriteElementString("LastActiveTab", settings.VideoControls.LastActiveTab); + textWriter.WriteElementString("WaveformDrawGrid", settings.VideoControls.WaveformDrawGrid.ToString()); + textWriter.WriteElementString("WaveformAllowOverlap", settings.VideoControls.WaveformAllowOverlap.ToString()); + textWriter.WriteElementString("WaveformFocusOnMouseEnter", settings.VideoControls.WaveformFocusOnMouseEnter.ToString()); + textWriter.WriteElementString("WaveformListViewFocusOnMouseEnter", settings.VideoControls.WaveformListViewFocusOnMouseEnter.ToString()); + textWriter.WriteElementString("WaveformBorderHitMs", settings.VideoControls.WaveformBorderHitMs.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("WaveformGridColor", settings.VideoControls.WaveformGridColor.ToArgb().ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("WaveformColor", settings.VideoControls.WaveformColor.ToArgb().ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("WaveformSelectedColor", settings.VideoControls.WaveformSelectedColor.ToArgb().ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("WaveformBackgroundColor", settings.VideoControls.WaveformBackgroundColor.ToArgb().ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("WaveformTextColor", settings.VideoControls.WaveformTextColor.ToArgb().ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("WaveformTextSize", settings.VideoControls.WaveformTextSize.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("WaveformTextBold", settings.VideoControls.WaveformTextBold.ToString()); + textWriter.WriteElementString("WaveformDoubleClickOnNonParagraphAction", settings.VideoControls.WaveformDoubleClickOnNonParagraphAction); + textWriter.WriteElementString("WaveformRightClickOnNonParagraphAction", settings.VideoControls.WaveformRightClickOnNonParagraphAction); + textWriter.WriteElementString("WaveformMouseWheelScrollUpIsForward", settings.VideoControls.WaveformMouseWheelScrollUpIsForward.ToString()); + textWriter.WriteElementString("GenerateSpectrogram", settings.VideoControls.GenerateSpectrogram.ToString()); + textWriter.WriteElementString("SpectrogramAppearance", settings.VideoControls.SpectrogramAppearance); + textWriter.WriteElementString("WaveformMinimumSampleRate", settings.VideoControls.WaveformMinimumSampleRate.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("WaveformSeeksSilenceDurationSeconds", settings.VideoControls.WaveformSeeksSilenceDurationSeconds.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("WaveformSeeksSilenceMaxVolume", settings.VideoControls.WaveformSeeksSilenceMaxVolume.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteEndElement(); + + textWriter.WriteStartElement("NetworkSettings", ""); + textWriter.WriteElementString("SessionKey", settings.NetworkSettings.SessionKey); + textWriter.WriteElementString("UserName", settings.NetworkSettings.UserName); + textWriter.WriteElementString("WebServiceUrl", settings.NetworkSettings.WebServiceUrl); + textWriter.WriteElementString("PollIntervalSeconds", settings.NetworkSettings.PollIntervalSeconds.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("NewMessageSound", settings.NetworkSettings.NewMessageSound); + textWriter.WriteEndElement(); + + textWriter.WriteStartElement("VobSubOcr", ""); + textWriter.WriteElementString("XOrMorePixelsMakesSpace", settings.VobSubOcr.XOrMorePixelsMakesSpace.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("AllowDifferenceInPercent", settings.VobSubOcr.AllowDifferenceInPercent.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("BlurayAllowDifferenceInPercent", settings.VobSubOcr.BlurayAllowDifferenceInPercent.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("LastImageCompareFolder", settings.VobSubOcr.LastImageCompareFolder); + textWriter.WriteElementString("LastModiLanguageId", settings.VobSubOcr.LastModiLanguageId.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("LastOcrMethod", settings.VobSubOcr.LastOcrMethod); + textWriter.WriteElementString("TesseractLastLanguage", settings.VobSubOcr.TesseractLastLanguage); + textWriter.WriteElementString("UseModiInTesseractForUnknownWords", settings.VobSubOcr.UseModiInTesseractForUnknownWords.ToString()); + textWriter.WriteElementString("UseItalicsInTesseract", settings.VobSubOcr.UseItalicsInTesseract.ToString()); + textWriter.WriteElementString("UseMusicSymbolsInTesseract", settings.VobSubOcr.UseMusicSymbolsInTesseract.ToString()); + textWriter.WriteElementString("RightToLeft", settings.VobSubOcr.RightToLeft.ToString()); + textWriter.WriteElementString("TopToBottom", settings.VobSubOcr.TopToBottom.ToString()); + textWriter.WriteElementString("DefaultMillisecondsForUnknownDurations", settings.VobSubOcr.DefaultMillisecondsForUnknownDurations.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("PromptForUnknownWords", settings.VobSubOcr.PromptForUnknownWords.ToString()); + textWriter.WriteElementString("GuessUnknownWords", settings.VobSubOcr.GuessUnknownWords.ToString()); + textWriter.WriteElementString("AutoBreakSubtitleIfMoreThanTwoLines", settings.VobSubOcr.AutoBreakSubtitleIfMoreThanTwoLines.ToString()); + textWriter.WriteElementString("ItalicFactor", settings.VobSubOcr.ItalicFactor.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("LineOcrDraw", settings.VobSubOcr.LineOcrDraw.ToString()); + textWriter.WriteElementString("LineOcrAdvancedItalic", settings.VobSubOcr.LineOcrAdvancedItalic.ToString()); + textWriter.WriteElementString("LineOcrLastLanguages", settings.VobSubOcr.LineOcrLastLanguages); + textWriter.WriteElementString("LineOcrLastSpellCheck", settings.VobSubOcr.LineOcrLastSpellCheck); + textWriter.WriteElementString("LineOcrXOrMorePixelsMakesSpace", settings.VobSubOcr.LineOcrXOrMorePixelsMakesSpace.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("LineOcrMinLineHeight", settings.VobSubOcr.LineOcrMinLineHeight.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("LineOcrMaxLineHeight", settings.VobSubOcr.LineOcrMaxLineHeight.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteEndElement(); + + textWriter.WriteStartElement("MultipleSearchAndReplaceList", ""); + foreach (var item in settings.MultipleSearchAndReplaceList) + { + textWriter.WriteStartElement("MultipleSearchAndReplaceItem", ""); + textWriter.WriteElementString("Enabled", item.Enabled.ToString()); + textWriter.WriteElementString("FindWhat", item.FindWhat); + textWriter.WriteElementString("ReplaceWith", item.ReplaceWith); + textWriter.WriteElementString("SearchType", item.SearchType); + textWriter.WriteEndElement(); + } + textWriter.WriteEndElement(); + + textWriter.WriteStartElement("Shortcuts", ""); + textWriter.WriteElementString("GeneralGoToFirstSelectedLine", settings.Shortcuts.GeneralGoToFirstSelectedLine); + textWriter.WriteElementString("GeneralGoToNextEmptyLine", settings.Shortcuts.GeneralGoToNextEmptyLine); + textWriter.WriteElementString("GeneralMergeSelectedLines", settings.Shortcuts.GeneralMergeSelectedLines); + textWriter.WriteElementString("GeneralMergeSelectedLinesOnlyFirstText", settings.Shortcuts.GeneralMergeSelectedLinesOnlyFirstText); + textWriter.WriteElementString("GeneralToggleTranslationMode", settings.Shortcuts.GeneralToggleTranslationMode); + textWriter.WriteElementString("GeneralSwitchOriginalAndTranslation", settings.Shortcuts.GeneralSwitchOriginalAndTranslation); + textWriter.WriteElementString("GeneralMergeOriginalAndTranslation", settings.Shortcuts.GeneralMergeOriginalAndTranslation); + textWriter.WriteElementString("GeneralGoToNextSubtitle", settings.Shortcuts.GeneralGoToNextSubtitle); + textWriter.WriteElementString("GeneralGoToPrevSubtitle", settings.Shortcuts.GeneralGoToPrevSubtitle); + textWriter.WriteElementString("GeneralGoToEndOfCurrentSubtitle", settings.Shortcuts.GeneralGoToEndOfCurrentSubtitle); + textWriter.WriteElementString("GeneralGoToStartOfCurrentSubtitle", settings.Shortcuts.GeneralGoToStartOfCurrentSubtitle); + textWriter.WriteElementString("GeneralPlayFirstSelected", settings.Shortcuts.GeneralPlayFirstSelected); + textWriter.WriteElementString("MainFileNew", settings.Shortcuts.MainFileNew); + textWriter.WriteElementString("MainFileOpen", settings.Shortcuts.MainFileOpen); + textWriter.WriteElementString("MainFileOpenKeepVideo", settings.Shortcuts.MainFileOpenKeepVideo); + textWriter.WriteElementString("MainFileSave", settings.Shortcuts.MainFileSave); + textWriter.WriteElementString("MainFileSaveOriginal", settings.Shortcuts.MainFileSaveOriginal); + textWriter.WriteElementString("MainFileSaveOriginalAs", settings.Shortcuts.MainFileSaveOriginalAs); + textWriter.WriteElementString("MainFileSaveAs", settings.Shortcuts.MainFileSaveAs); + textWriter.WriteElementString("MainFileSaveAll", settings.Shortcuts.MainFileSaveAll); + textWriter.WriteElementString("MainFileExportEbu", settings.Shortcuts.MainFileExportEbu); + textWriter.WriteElementString("MainEditUndo", settings.Shortcuts.MainEditUndo); + textWriter.WriteElementString("MainEditRedo", settings.Shortcuts.MainEditRedo); + textWriter.WriteElementString("MainEditFind", settings.Shortcuts.MainEditFind); + textWriter.WriteElementString("MainEditFindNext", settings.Shortcuts.MainEditFindNext); + textWriter.WriteElementString("MainEditReplace", settings.Shortcuts.MainEditReplace); + textWriter.WriteElementString("MainEditMultipleReplace", settings.Shortcuts.MainEditMultipleReplace); + textWriter.WriteElementString("MainEditGoToLineNumber", settings.Shortcuts.MainEditGoToLineNumber); + textWriter.WriteElementString("MainEditRightToLeft", settings.Shortcuts.MainEditRightToLeft); + textWriter.WriteElementString("MainToolsFixCommonErrors", settings.Shortcuts.MainToolsFixCommonErrors); + textWriter.WriteElementString("MainToolsFixCommonErrorsPreview", settings.Shortcuts.MainToolsFixCommonErrorsPreview); + textWriter.WriteElementString("MainToolsMergeShortLines", settings.Shortcuts.MainToolsMergeShortLines); + textWriter.WriteElementString("MainToolsSplitLongLines", settings.Shortcuts.MainToolsSplitLongLines); + textWriter.WriteElementString("MainToolsRenumber", settings.Shortcuts.MainToolsRenumber); + textWriter.WriteElementString("MainToolsRemoveTextForHI", settings.Shortcuts.MainToolsRemoveTextForHI); + textWriter.WriteElementString("MainToolsChangeCasing", settings.Shortcuts.MainToolsChangeCasing); + textWriter.WriteElementString("MainToolsAutoDuration", settings.Shortcuts.MainToolsAutoDuration); + textWriter.WriteElementString("MainToolsBatchConvert", settings.Shortcuts.MainToolsBatchConvert); + textWriter.WriteElementString("MainToolsBeamer", settings.Shortcuts.MainToolsBeamer); + textWriter.WriteElementString("MainToolsToggleTranslationOriginalInPreviews", settings.Shortcuts.MainEditToggleTranslationOriginalInPreviews); + textWriter.WriteElementString("MainEditInverseSelection", settings.Shortcuts.MainEditInverseSelection); + textWriter.WriteElementString("MainEditModifySelection", settings.Shortcuts.MainEditModifySelection); + textWriter.WriteElementString("MainVideoPause", settings.Shortcuts.MainVideoPause); + textWriter.WriteElementString("MainVideoPlayPauseToggle", settings.Shortcuts.MainVideoPlayPauseToggle); + textWriter.WriteElementString("MainVideoShowHideVideo", settings.Shortcuts.MainVideoShowHideVideo); + textWriter.WriteElementString("MainVideoToggleVideoControls", settings.Shortcuts.MainVideoToggleVideoControls); + textWriter.WriteElementString("MainVideo1FrameLeft", settings.Shortcuts.MainVideo1FrameLeft); + textWriter.WriteElementString("MainVideo1FrameRight", settings.Shortcuts.MainVideo1FrameRight); + textWriter.WriteElementString("MainVideo100MsLeft", settings.Shortcuts.MainVideo100MsLeft); + textWriter.WriteElementString("MainVideo100MsRight", settings.Shortcuts.MainVideo100MsRight); + textWriter.WriteElementString("MainVideo500MsLeft", settings.Shortcuts.MainVideo500MsLeft); + textWriter.WriteElementString("MainVideo500MsRight", settings.Shortcuts.MainVideo500MsRight); + textWriter.WriteElementString("MainVideo1000MsLeft", settings.Shortcuts.MainVideo1000MsLeft); + textWriter.WriteElementString("MainVideo1000MsRight", settings.Shortcuts.MainVideo1000MsRight); + textWriter.WriteElementString("MainVideoFullscreen", settings.Shortcuts.MainVideoFullscreen); + textWriter.WriteElementString("MainSpellCheck", settings.Shortcuts.MainSpellCheck); + textWriter.WriteElementString("MainSpellCheckFindDoubleWords", settings.Shortcuts.MainSpellCheckFindDoubleWords); + textWriter.WriteElementString("MainSpellCheckAddWordToNames", settings.Shortcuts.MainSpellCheckAddWordToNames); + textWriter.WriteElementString("MainSynchronizationAdjustTimes", settings.Shortcuts.MainSynchronizationAdjustTimes); + textWriter.WriteElementString("MainSynchronizationVisualSync", settings.Shortcuts.MainSynchronizationVisualSync); + textWriter.WriteElementString("MainSynchronizationPointSync", settings.Shortcuts.MainSynchronizationPointSync); + textWriter.WriteElementString("MainSynchronizationChangeFrameRate", settings.Shortcuts.MainSynchronizationChangeFrameRate); + textWriter.WriteElementString("MainListViewItalic", settings.Shortcuts.MainListViewItalic); + textWriter.WriteElementString("MainListViewToggleDashes", settings.Shortcuts.MainListViewToggleDashes); + textWriter.WriteElementString("MainListViewAlignment", settings.Shortcuts.MainListViewAlignment); + textWriter.WriteElementString("MainListViewCopyText", settings.Shortcuts.MainListViewCopyText); + textWriter.WriteElementString("MainListViewCopyTextFromOriginalToCurrent", settings.Shortcuts.MainListViewCopyTextFromOriginalToCurrent); + textWriter.WriteElementString("MainListViewAutoDuration", settings.Shortcuts.MainListViewAutoDuration); + textWriter.WriteElementString("MainListViewColumnDeleteText", settings.Shortcuts.MainListViewColumnDeleteText); + textWriter.WriteElementString("MainListViewColumnInsertText", settings.Shortcuts.MainListViewColumnInsertText); + textWriter.WriteElementString("MainListViewColumnPaste", settings.Shortcuts.MainListViewColumnPaste); + textWriter.WriteElementString("MainListViewFocusWaveform", settings.Shortcuts.MainListViewFocusWaveform); + textWriter.WriteElementString("MainListViewGoToNextError", settings.Shortcuts.MainListViewGoToNextError); + textWriter.WriteElementString("MainEditReverseStartAndEndingForRTL", settings.Shortcuts.MainEditReverseStartAndEndingForRTL); + textWriter.WriteElementString("MainTextBoxItalic", settings.Shortcuts.MainTextBoxItalic); + textWriter.WriteElementString("MainTextBoxSplitAtCursor", settings.Shortcuts.MainTextBoxSplitAtCursor); + textWriter.WriteElementString("MainTextBoxMoveLastWordDown", settings.Shortcuts.MainTextBoxMoveLastWordDown); + textWriter.WriteElementString("MainTextBoxMoveFirstWordFromNextUp", settings.Shortcuts.MainTextBoxMoveFirstWordFromNextUp); + textWriter.WriteElementString("MainTextBoxSelectionToLower", settings.Shortcuts.MainTextBoxSelectionToLower); + textWriter.WriteElementString("MainTextBoxSelectionToUpper", settings.Shortcuts.MainTextBoxSelectionToUpper); + textWriter.WriteElementString("MainTextBoxToggleAutoDuration", settings.Shortcuts.MainTextBoxToggleAutoDuration); + textWriter.WriteElementString("MainCreateInsertSubAtVideoPos", settings.Shortcuts.MainCreateInsertSubAtVideoPos); + textWriter.WriteElementString("MainCreatePlayFromJustBefore", settings.Shortcuts.MainCreatePlayFromJustBefore); + textWriter.WriteElementString("MainCreateSetStart", settings.Shortcuts.MainCreateSetStart); + textWriter.WriteElementString("MainCreateSetEnd", settings.Shortcuts.MainCreateSetEnd); + textWriter.WriteElementString("MainCreateSetEndAddNewAndGoToNew", settings.Shortcuts.MainCreateSetEndAddNewAndGoToNew); + textWriter.WriteElementString("MainCreateStartDownEndUp", settings.Shortcuts.MainCreateStartDownEndUp); + textWriter.WriteElementString("MainAdjustSetStartAndOffsetTheRest", settings.Shortcuts.MainAdjustSetStartAndOffsetTheRest); + textWriter.WriteElementString("MainAdjustSetEndAndOffsetTheRest", settings.Shortcuts.MainAdjustSetEndAndOffsetTheRest); + textWriter.WriteElementString("MainAdjustSetEndAndOffsetTheRestAndGoToNext", settings.Shortcuts.MainAdjustSetEndAndOffsetTheRestAndGoToNext); + textWriter.WriteElementString("MainAdjustSetEndAndGotoNext", settings.Shortcuts.MainAdjustSetEndAndGotoNext); + textWriter.WriteElementString("MainAdjustViaEndAutoStartAndGoToNext", settings.Shortcuts.MainAdjustViaEndAutoStartAndGoToNext); + textWriter.WriteElementString("MainAdjustSetStartAutoDurationAndGoToNext", settings.Shortcuts.MainAdjustSetStartAutoDurationAndGoToNext); + textWriter.WriteElementString("MainAdjustSetEndNextStartAndGoToNext", settings.Shortcuts.MainAdjustSetEndNextStartAndGoToNext); + textWriter.WriteElementString("MainAdjustStartDownEndUpAndGoToNext", settings.Shortcuts.MainAdjustStartDownEndUpAndGoToNext); + textWriter.WriteElementString("MainAdjustSetStart", settings.Shortcuts.MainAdjustSetStart); + textWriter.WriteElementString("MainAdjustSetStartKeepDuration", settings.Shortcuts.MainAdjustSetStartKeepDuration); + textWriter.WriteElementString("MainAdjustSetEnd", settings.Shortcuts.MainAdjustSetEnd); + textWriter.WriteElementString("MainAdjustSelected100MsForward", settings.Shortcuts.MainAdjustSelected100MsForward); + textWriter.WriteElementString("MainAdjustSelected100MsBack", settings.Shortcuts.MainAdjustSelected100MsBack); + textWriter.WriteElementString("MainInsertAfter", settings.Shortcuts.MainInsertAfter); + textWriter.WriteElementString("MainTextBoxInsertAfter", settings.Shortcuts.MainTextBoxInsertAfter); + textWriter.WriteElementString("MainTextBoxAutoBreak", settings.Shortcuts.MainTextBoxAutoBreak); + textWriter.WriteElementString("MainTextBoxUnbreak", settings.Shortcuts.MainTextBoxUnbreak); + textWriter.WriteElementString("MainWaveformInsertAtCurrentPosition", settings.Shortcuts.MainWaveformInsertAtCurrentPosition); + textWriter.WriteElementString("MainInsertBefore", settings.Shortcuts.MainInsertBefore); + textWriter.WriteElementString("MainMergeDialog", settings.Shortcuts.MainMergeDialog); + textWriter.WriteElementString("MainToggleFocus", settings.Shortcuts.MainToggleFocus); + textWriter.WriteElementString("WaveformVerticalZoom", settings.Shortcuts.WaveformVerticalZoom); + textWriter.WriteElementString("WaveformVerticalZoomOut", settings.Shortcuts.WaveformVerticalZoomOut); + textWriter.WriteElementString("WaveformZoomIn", settings.Shortcuts.WaveformZoomIn); + textWriter.WriteElementString("WaveformZoomOut", settings.Shortcuts.WaveformZoomOut); + textWriter.WriteElementString("WaveformPlaySelection", settings.Shortcuts.WaveformPlaySelection); + textWriter.WriteElementString("WaveformSearchSilenceForward", settings.Shortcuts.WaveformSearchSilenceForward); + textWriter.WriteElementString("WaveformSearchSilenceBack", settings.Shortcuts.WaveformSearchSilenceBack); + textWriter.WriteElementString("WaveformAddTextHere", settings.Shortcuts.WaveformAddTextHere); + textWriter.WriteElementString("WaveformFocusListView", settings.Shortcuts.WaveformFocusListView); + textWriter.WriteElementString("MainTranslateCustomSearch1", settings.Shortcuts.MainTranslateCustomSearch1); + textWriter.WriteElementString("MainTranslateCustomSearch2", settings.Shortcuts.MainTranslateCustomSearch2); + textWriter.WriteElementString("MainTranslateCustomSearch3", settings.Shortcuts.MainTranslateCustomSearch3); + textWriter.WriteElementString("MainTranslateCustomSearch4", settings.Shortcuts.MainTranslateCustomSearch4); + textWriter.WriteElementString("MainTranslateCustomSearch5", settings.Shortcuts.MainTranslateCustomSearch5); + textWriter.WriteElementString("MainTranslateCustomSearch6", settings.Shortcuts.MainTranslateCustomSearch6); + textWriter.WriteEndElement(); + + textWriter.WriteStartElement("RemoveTextForHearingImpaired", ""); + textWriter.WriteElementString("RemoveTextBetweenBrackets", settings.RemoveTextForHearingImpaired.RemoveTextBetweenBrackets.ToString()); + textWriter.WriteElementString("RemoveTextBetweenParentheses", settings.RemoveTextForHearingImpaired.RemoveTextBetweenParentheses.ToString()); + textWriter.WriteElementString("RemoveTextBetweenCurlyBrackets", settings.RemoveTextForHearingImpaired.RemoveTextBetweenCurlyBrackets.ToString()); + textWriter.WriteElementString("RemoveTextBetweenQuestionMarks", settings.RemoveTextForHearingImpaired.RemoveTextBetweenQuestionMarks.ToString()); + textWriter.WriteElementString("RemoveTextBetweenCustom", settings.RemoveTextForHearingImpaired.RemoveTextBetweenCustom.ToString()); + textWriter.WriteElementString("RemoveTextBetweenCustomBefore", settings.RemoveTextForHearingImpaired.RemoveTextBetweenCustomBefore); + textWriter.WriteElementString("RemoveTextBetweenCustomAfter", settings.RemoveTextForHearingImpaired.RemoveTextBetweenCustomAfter); + textWriter.WriteElementString("RemoveTextBetweenOnlySeperateLines", settings.RemoveTextForHearingImpaired.RemoveTextBetweenOnlySeperateLines.ToString()); + textWriter.WriteElementString("RemoveTextBeforeColon", settings.RemoveTextForHearingImpaired.RemoveTextBeforeColon.ToString()); + textWriter.WriteElementString("RemoveTextBeforeColonOnlyIfUppercase", settings.RemoveTextForHearingImpaired.RemoveTextBeforeColonOnlyIfUppercase.ToString()); + textWriter.WriteElementString("RemoveTextBeforeColonOnlyOnSeparateLine", settings.RemoveTextForHearingImpaired.RemoveTextBeforeColonOnlyOnSeparateLine.ToString()); + textWriter.WriteElementString("RemoveInterjections", settings.RemoveTextForHearingImpaired.RemoveInterjections.ToString()); + textWriter.WriteElementString("RemoveIfAllUppercase", settings.RemoveTextForHearingImpaired.RemoveIfAllUppercase.ToString()); + textWriter.WriteElementString("RemoveIfContains", settings.RemoveTextForHearingImpaired.RemoveIfContains.ToString()); + textWriter.WriteElementString("RemoveIfContainsText", settings.RemoveTextForHearingImpaired.RemoveIfContainsText); + textWriter.WriteEndElement(); + + textWriter.WriteStartElement("SubtitleBeaming", ""); + textWriter.WriteElementString("FontName", settings.SubtitleBeaming.FontName); + textWriter.WriteElementString("FontColor", settings.SubtitleBeaming.FontColor.ToArgb().ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("FontSize", settings.SubtitleBeaming.FontSize.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("BorderColor", settings.SubtitleBeaming.BorderColor.ToArgb().ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("BorderWidth", settings.SubtitleBeaming.BorderWidth.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteEndElement(); + + textWriter.WriteEndElement(); + + textWriter.WriteEndDocument(); + textWriter.Flush(); + + try + { + File.WriteAllText(fileName, sb.ToString().Replace("encoding=\"utf-16\"", "encoding=\"utf-8\""), Encoding.UTF8); + } + catch + { + } + } + } + + } +} \ No newline at end of file diff --git a/libse/SpellCheckWord.cs b/libse/SpellCheckWord.cs new file mode 100644 index 000000000..c1d102a69 --- /dev/null +++ b/libse/SpellCheckWord.cs @@ -0,0 +1,8 @@ +namespace Nikse.SubtitleEdit.Core +{ + public class SpellCheckWord + { + public int Index { get; set; } + public string Text { get; set; } + } +} diff --git a/libse/SsaStyle.cs b/libse/SsaStyle.cs new file mode 100644 index 000000000..808570851 --- /dev/null +++ b/libse/SsaStyle.cs @@ -0,0 +1,205 @@ +using System; +using System.Drawing; +using System.Text; +using Nikse.SubtitleEdit.Core.SubtitleFormats; + +namespace Nikse.SubtitleEdit.Core +{ + public class SsaStyle + { + public string Name { get; set; } + public string FontName { get; set; } + public int FontSize { get; set; } + public bool Italic { get; set; } + public bool Bold { get; set; } + public bool Underline { get; set; } + public Color Primary { get; set; } + public Color Secondary { get; set; } + public Color Tertiary { get; set; } + public Color Outline { get; set; } + public Color Background { get; set; } + public int ShadowWidth { get; set; } + public int OutlineWidth { get; set; } + public string Alignment { get; set; } + public int MarginLeft { get; set; } + public int MarginRight { get; set; } + public int MarginVertical { get; set; } + public string BorderStyle { get; set; } + public string RawLine { get; set; } + public bool LoadedFromHeader { get; set; } + + public SsaStyle() + { + FontName = Configuration.Settings.SubtitleSettings.SsaFontName; + FontSize = (int)Configuration.Settings.SubtitleSettings.SsaFontSize; + Primary = Color.FromArgb(Configuration.Settings.SubtitleSettings.SsaFontColorArgb); + Secondary = Color.Yellow; + Outline = Color.Black; + Background = Color.Black; + Alignment = "2"; + OutlineWidth = Configuration.Settings.SubtitleSettings.SsaOutline; + ShadowWidth = Configuration.Settings.SubtitleSettings.SsaShadow; + MarginLeft = 10; + MarginRight = 10; + MarginVertical = 10; + BorderStyle = "1"; + if (Configuration.Settings.SubtitleSettings.SsaOpaqueBox) + BorderStyle = "3"; + RawLine = string.Empty; + LoadedFromHeader = false; + } + + public SsaStyle(SsaStyle ssaStyle) + { + Name = ssaStyle.Name; + FontName = ssaStyle.FontName; + FontSize = ssaStyle.FontSize; + + Italic = ssaStyle.Italic; + Bold = ssaStyle.Bold; + Underline = ssaStyle.Underline; + + Primary = ssaStyle.Primary; + Secondary = ssaStyle.Secondary; + Tertiary = ssaStyle.Tertiary; + Outline = ssaStyle.Outline; + Background = ssaStyle.Background; + + ShadowWidth = ssaStyle.ShadowWidth; + OutlineWidth = ssaStyle.OutlineWidth; + + Alignment = ssaStyle.Alignment; + MarginLeft = ssaStyle.MarginLeft; + MarginRight = ssaStyle.MarginRight; + MarginVertical = ssaStyle.MarginVertical; + + BorderStyle = ssaStyle.BorderStyle; + RawLine = ssaStyle.RawLine; + LoadedFromHeader = ssaStyle.LoadedFromHeader; + } + + public string ToRawSsa(string styleFormat) + { + var sb = new StringBuilder(); + sb.Append("Style: "); + var format = styleFormat.ToLower().Substring(8).Split(','); + for (int i = 0; i < format.Length; i++) + { + string f = format[i].Trim().ToLower(); + if (f == "name") + sb.Append(Name); + else if (f == "fontname") + sb.Append(FontName); + else if (f == "fontsize") + sb.Append(FontSize); + else if (f == "primarycolour") + sb.Append(ColorTranslator.ToWin32(Primary)); + else if (f == "secondarycolour") + sb.Append(ColorTranslator.ToWin32(Secondary)); + else if (f == "tertiarycolour") + sb.Append(ColorTranslator.ToWin32(Tertiary)); + else if (f == "outlinecolour") + sb.Append(ColorTranslator.ToWin32(Outline)); + else if (f == "backcolour") + sb.Append(ColorTranslator.ToWin32(Background)); + else if (f == "bold") + sb.Append(Convert.ToInt32(Bold)); + else if (f == "italic") + sb.Append(Convert.ToInt32(Italic)); + else if (f == "underline") + sb.Append(Convert.ToInt32(Underline)); + else if (f == "outline") + sb.Append(Outline); + else if (f == "shadow") + sb.Append(OutlineWidth); + else if (f == "shadow") + sb.Append(ShadowWidth); + else if (f == "marginl") + sb.Append(MarginLeft); + else if (f == "marginr") + sb.Append(MarginRight); + else if (f == "marginv") + sb.Append(MarginVertical); + else if (f == "borderstyle") + sb.Append(BorderStyle); + else if (f == "encoding") + sb.Append('1'); + else if (f == "strikeout") + sb.Append('0'); + else if (f == "scalex") + sb.Append("100"); + else if (f == "scaley") + sb.Append("100"); + else if (f == "spacing") + sb.Append('0'); + else if (f == "angle") + sb.Append('0'); + sb.Append(','); + } + string s = sb.ToString().Trim(); + return s.Substring(0, s.Length - 1); + } + + public string ToRawAss(string styleFormat) + { + var sb = new StringBuilder(); + sb.Append("Style: "); + var format = styleFormat.ToLower().Substring(8).Split(','); + for (int i = 0; i < format.Length; i++) + { + string f = format[i].Trim().ToLower(); + if (f == "name") + sb.Append(Name); + else if (f == "fontname") + sb.Append(FontName); + else if (f == "fontsize") + sb.Append(FontSize); + else if (f == "primarycolour") + sb.Append(AdvancedSubStationAlpha.GetSsaColorString(Primary)); + else if (f == "secondarycolour") + sb.Append(AdvancedSubStationAlpha.GetSsaColorString(Secondary)); + else if (f == "tertiarycolour") + sb.Append(AdvancedSubStationAlpha.GetSsaColorString(Tertiary)); + else if (f == "outlinecolour") + sb.Append(AdvancedSubStationAlpha.GetSsaColorString(Outline)); + else if (f == "backcolour") + sb.Append(AdvancedSubStationAlpha.GetSsaColorString(Background)); + else if (f == "bold") + sb.Append(Convert.ToInt32(Bold)); + else if (f == "italic") + sb.Append(Convert.ToInt32(Italic)); + else if (f == "underline") + sb.Append(Convert.ToInt32(Underline)); + else if (f == "outline") + sb.Append(OutlineWidth); + else if (f == "shadow") + sb.Append(ShadowWidth); + else if (f == "alignment") + sb.Append(Alignment); + else if (f == "marginl") + sb.Append(MarginLeft); + else if (f == "marginr") + sb.Append(MarginRight); + else if (f == "marginv") + sb.Append(MarginVertical); + else if (f == "borderstyle") + sb.Append(BorderStyle); + else if (f == "encoding") + sb.Append('1'); + else if (f == "strikeout") + sb.Append('0'); + else if (f == "scalex") + sb.Append("100"); + else if (f == "scaley") + sb.Append("100"); + else if (f == "spacing") + sb.Append('0'); + else if (f == "angle") + sb.Append('0'); + sb.Append(','); + } + string s = sb.ToString().Trim(); + return s.Substring(0, s.Length - 1); + } + } +} \ No newline at end of file diff --git a/libse/StringExtensions.cs b/libse/StringExtensions.cs new file mode 100644 index 000000000..98878f38e --- /dev/null +++ b/libse/StringExtensions.cs @@ -0,0 +1,167 @@ +using System; +using System.Text; + +namespace Nikse.SubtitleEdit.Core +{ + public static class StringExtensions + { + public static bool LineStartsWithHtmlTag(this string text, bool threeLengthTag, bool includeFont = false) + { + if (text == null || (!threeLengthTag && !includeFont)) + return false; + return StartsWithHtmlTag(text, threeLengthTag, includeFont); + } + + public static bool LineEndsWithHtmlTag(this string text, bool threeLengthTag, bool includeFont = false) + { + if (text == null) + return false; + + var len = text.Length; + if (len < 6 || text[len - 1] != '>') + return false; + + //
+ if (threeLengthTag && len > 3 && text[len - 4] == '<' && text[len - 3] == '/') + return true; + if (includeFont && len > 8 && text[len - 7] == '<' && text[len - 6] == '/') + return true; + return false; + + } + + public static bool LineBreakStartsWithHtmlTag(this string text, bool threeLengthTag, bool includeFont = false) + { + if (text == null || (!threeLengthTag && !includeFont)) + return false; + var newLineIdx = text.IndexOf(Environment.NewLine, StringComparison.Ordinal); + if (newLineIdx < 0 || text.Length < newLineIdx + 5) + return false; + text = text.Substring(newLineIdx + 2); + return StartsWithHtmlTag(text, threeLengthTag, includeFont); + } + + private static bool StartsWithHtmlTag(string text, bool threeLengthTag, bool includeFont) + { + if (threeLengthTag && text.Length >= 3 && text[0] == '<' && text[2] == '>' && (text[1] == 'i' || text[1] == 'I' || text[1] == 'u' || text[1] == 'U' || text[1] == 'b' || text[1] == 'B')) + return true; + if (includeFont && text.Length > 5 && text.StartsWith("', 5) >= 5; // or + return false; + } + + public static bool StartsWith(this string s, char c) + { + return s.Length > 0 && s[0] == c; + } + + public static bool StartsWith(this StringBuilder sb, char c) + { + return sb.Length > 0 && sb[0] == c; + } + + public static bool EndsWith(this string s, char c) + { + return s.Length > 0 && s[s.Length - 1] == c; + } + + public static bool EndsWith(this StringBuilder sb, char c) + { + return sb.Length > 0 && sb[sb.Length - 1] == c; + } + + public static bool Contains(this string source, char value) + { + return source.IndexOf(value) >= 0; + } + + public static bool Contains(this string source, char[] value) + { + return source.IndexOfAny(value) >= 0; + } + + public static bool Contains(this string source, string value, StringComparison comparisonType) + { + return source.IndexOf(value, comparisonType) >= 0; + } + + public static string[] SplitToLines(this string source) + { + return source.Replace("\r\n", "\n").Replace('\r', '\n').Split('\n'); + } + + // http://www.codeproject.com/Articles/43726/Optimizing-string-operations-in-C + public static int FastIndexOf(this string source, string pattern) + { + if (pattern == null) throw new ArgumentNullException(); + if (pattern.Length == 0) return 0; + if (pattern.Length == 1) return source.IndexOf(pattern[0]); + int limit = source.Length - pattern.Length + 1; + if (limit < 1) return -1; + // Store the first 2 characters of "pattern" + char c0 = pattern[0]; + char c1 = pattern[1]; + // Find the first occurrence of the first character + int first = source.IndexOf(c0, 0, limit); + while (first != -1) + { + // Check if the following character is the same like + // the 2nd character of "pattern" + if (source[first + 1] != c1) + { + first = source.IndexOf(c0, ++first, limit - first); + continue; + } + // Check the rest of "pattern" (starting with the 3rd character) + bool found = true; + for (var j = 2; j < pattern.Length; j++) + if (source[first + j] != pattern[j]) + { + found = false; + break; + } + // If the whole word was found, return its index, otherwise try again + if (found) return first; + first = source.IndexOf(c0, ++first, limit - first); + } + return -1; + } + + public static int IndexOfAny(this string s, string[] words, StringComparison comparionType) + { + if (words == null || string.IsNullOrEmpty(s)) + return -1; + for (int i = 0; i < words.Length; i++) + { + var idx = s.IndexOf(words[i], comparionType); + if (idx >= 0) + return idx; + } + return -1; + } + + public static string FixExtraSpaces(this string s) + { + if (string.IsNullOrEmpty(s)) + return s; + + while (s.Contains(" ")) + s = s.Replace(" ", " "); + s = s.Replace(" " + Environment.NewLine, Environment.NewLine); + return s.Replace(Environment.NewLine + " ", Environment.NewLine); + } + + public static bool ContainsLetter(this string s) + { + if (string.IsNullOrWhiteSpace(s)) + return false; + + foreach (var c in s) + { + if (char.IsLetter(c)) + return true; + } + return false; + } + } +} \ No newline at end of file diff --git a/libse/StripableText.cs b/libse/StripableText.cs new file mode 100644 index 000000000..171067529 --- /dev/null +++ b/libse/StripableText.cs @@ -0,0 +1,285 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Nikse.SubtitleEdit.Core +{ + public class StripableText + { + public string Pre { get; set; } + public string Post { get; set; } + public string StrippedText { get; set; } + public string OriginalText { get; private set; } + + public string MergedString + { + get { return Pre + StrippedText + Post; } + } + + public StripableText(string text) + : this(text, " >-\"”“['‘`´¶(♪¿¡.…—", " -\"”“]'`´¶)♪.!?:…—") + { + } + + public StripableText(string text, string stripStartCharacters, string stripEndCharacters) + { + OriginalText = text; + + Pre = string.Empty; + if (text.Length > 0 && ("<{" + stripStartCharacters).Contains(text[0])) + { + int beginLength; + do + { + beginLength = text.Length; + + while (text.Length > 0 && stripStartCharacters.Contains(text[0])) + { + Pre += text[0]; + text = text.Remove(0, 1); + } + + // ASS/SSA codes like {\an9} + int endIndex = text.IndexOf('}'); + if (endIndex > 0 && text.StartsWith("{\\", StringComparison.Ordinal)) + { + int nextStartIndex = text.IndexOf('{', 2); + if (nextStartIndex == -1 || nextStartIndex > endIndex) + { + endIndex++; + Pre += text.Substring(0, endIndex); + text = text.Remove(0, endIndex); + } + } + + // tags like or + endIndex = text.IndexOf('>'); + if (text.StartsWith('<') && endIndex >= 2) + { + endIndex++; + Pre += text.Substring(0, endIndex); + text = text.Remove(0, endIndex); + } + } + while (text.Length < beginLength); + } + + Post = string.Empty; + if (text.Length > 0 && (">" + stripEndCharacters).Contains(text[text.Length - 1])) + { + int beginLength; + do + { + beginLength = text.Length; + + while (text.Length > 0 && stripEndCharacters.Contains(text[text.Length - 1])) + { + Post = text[text.Length - 1] + Post; + text = text.Substring(0, text.Length - 1); + } + + if (text.EndsWith('>')) + { + // tags + if (text.EndsWith("", StringComparison.OrdinalIgnoreCase) || + text.EndsWith("", StringComparison.OrdinalIgnoreCase) || + text.EndsWith("", StringComparison.OrdinalIgnoreCase)) + { + Post = text.Substring(text.Length - 4) + Post; + text = text.Substring(0, text.Length - 4); + } + + // tag + if (text.EndsWith("", StringComparison.OrdinalIgnoreCase)) + { + Post = text.Substring(text.Length - 7) + Post; + text = text.Substring(0, text.Length - 7); + } + } + } + while (text.Length < beginLength); + } + + StrippedText = text; + } + + private static string GetAndInsertNextId(List replaceIds, List replaceNames, string name) + { + int i = 0; + string id = string.Format("_@{0}_", i); + while (replaceIds.Contains(id)) + { + i++; + id = string.Format("_@{0}_", i); + } + replaceIds.Add(id); + replaceNames.Add(name); + return id; + } + + private void ReplaceNames1Remove(List namesEtc, List replaceIds, List replaceNames, List originalNames) + { + if (Post.StartsWith('.')) + { + StrippedText += "."; + Post = Post.Remove(0, 1); + } + + string lower = StrippedText.ToLower(); + + foreach (string name in namesEtc) + { + int start = lower.IndexOf(name, StringComparison.OrdinalIgnoreCase); + while (start >= 0 && start < lower.Length) + { + bool startOk = (start == 0) || (lower[start - 1] == ' ') || (lower[start - 1] == '-') || + (lower[start - 1] == '"') || (lower[start - 1] == '\'') || (lower[start - 1] == '>') || + Environment.NewLine.EndsWith(lower[start - 1]); + + if (startOk && string.CompareOrdinal(name, "Don") == 0 && lower.Substring(start).StartsWith("don't")) + startOk = false; + + if (startOk) + { + int end = start + name.Length; + bool endOk = end <= lower.Length; + if (endOk) + endOk = end == lower.Length || (@" ,.!?:;')- <""" + Environment.NewLine).Contains(lower[end]); + + if (endOk && StrippedText.Length >= start + name.Length) + { + string originalName = StrippedText.Substring(start, name.Length); + originalNames.Add(originalName); + StrippedText = StrippedText.Remove(start, name.Length); + StrippedText = StrippedText.Insert(start, GetAndInsertNextId(replaceIds, replaceNames, name)); + lower = StrippedText.ToLower(); + } + } + if (start + 3 > lower.Length) + start = lower.Length + 1; + else + start = lower.IndexOf(name.ToLower(), start + 3, StringComparison.Ordinal); + } + } + + if (StrippedText.EndsWith('.')) + { + Post = "." + Post; + StrippedText = StrippedText.TrimEnd('.'); + } + } + + private void ReplaceNames2Fix(List replaceIds, List replaceNames) + { + for (int i = 0; i < replaceIds.Count; i++) + { + StrippedText = StrippedText.Replace(replaceIds[i], replaceNames[i]); + } + } + + public void FixCasing(List namesEtc, bool changeNameCases, bool makeUppercaseAfterBreak, bool checkLastLine, string lastLine) + { + var replaceIds = new List(); + var replaceNames = new List(); + var originalNames = new List(); + ReplaceNames1Remove(namesEtc, replaceIds, replaceNames, originalNames); + + if (checkLastLine) + { + string s = HtmlUtil.RemoveHtmlTags(lastLine).TrimEnd().TrimEnd('\"').TrimEnd(); + + bool startWithUppercase = string.IsNullOrEmpty(s) || + s.EndsWith('.') || + s.EndsWith('!') || + s.EndsWith('?') || + s.EndsWith(". ♪", StringComparison.Ordinal) || + s.EndsWith("! ♪", StringComparison.Ordinal) || + s.EndsWith("? ♪", StringComparison.Ordinal) || + s.EndsWith(']') || + s.EndsWith(')') || + s.EndsWith(':'); + + // start with uppercase after music symbol - but only if next line does not start with music symbol + if (!startWithUppercase && (s.EndsWith('♪') || s.EndsWith('♫'))) + { + if (!Pre.Contains(new[] { '♪', '♫' })) + startWithUppercase = true; + } + + if (startWithUppercase && StrippedText.Length > 0 && !Pre.Contains("...")) + { + StrippedText = char.ToUpper(StrippedText[0]) + StrippedText.Substring(1); + } + } + + if (makeUppercaseAfterBreak && StrippedText.Contains(new[] { '.', '!', '?', ':', ';', ')', ']', '}', '(', '[', '{' })) + { + const string breakAfterChars = @".!?:;)]}([{"; + + var sb = new StringBuilder(); + bool lastWasBreak = false; + for (int i = 0; i < StrippedText.Length; i++) + { + var s = StrippedText[i]; + if (lastWasBreak) + { + if (("\"`´'()<>!?.- " + Environment.NewLine).Contains(s)) + { + sb.Append(s); + } + else if ((sb.EndsWith('<') || sb.ToString().EndsWith("') + { // tags + sb.Append(s); + } + else if (sb.EndsWith('<') && s == '/' && i + 2 < StrippedText.Length && StrippedText[i + 2] == '>') + { // tags + sb.Append(s); + } + else if (sb.ToString().EndsWith("... ", StringComparison.Ordinal)) + { + sb.Append(s); + lastWasBreak = false; + } + else + { + if (breakAfterChars.Contains(s)) + { + sb.Append(s); + } + else + { + lastWasBreak = false; + sb.Append(char.ToUpper(s)); + } + } + } + else + { + sb.Append(s); + if (breakAfterChars.Contains(s)) + { + var idx = sb.ToString().IndexOf('['); + if (s == ']' && idx > 1) + { // I [Motor roaring] love you! + string temp = sb.ToString(0, idx - 1).Trim(); + if (temp.Length > 0 && !Utilities.LowercaseLetters.Contains(temp[temp.Length - 1])) + lastWasBreak = true; + } + else + { + lastWasBreak = true; + } + } + } + } + StrippedText = sb.ToString(); + } + + if (changeNameCases) + ReplaceNames2Fix(replaceIds, replaceNames); + else + ReplaceNames2Fix(replaceIds, originalNames); + } + + } +} \ No newline at end of file diff --git a/libse/Subtitle.cs b/libse/Subtitle.cs new file mode 100644 index 000000000..359f25751 --- /dev/null +++ b/libse/Subtitle.cs @@ -0,0 +1,530 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Windows.Forms; +using Nikse.Core; +using Nikse.Core.Enums; +using Nikse.SubtitleEdit.Core.SubtitleFormats; + +namespace Nikse.SubtitleEdit.Core +{ + public class Subtitle + { + private List _paragraphs; + private List _history; + private SubtitleFormat _format; + private bool _wasLoadedWithFrameNumbers; + public string Header { get; set; } + public string Footer { get; set; } + + public string FileName { get; set; } + + public const int MaximumHistoryItems = 100; + + public SubtitleFormat OriginalFormat + { + get + { + return _format; + } + } + + public List HistoryItems + { + get { return _history; } + } + + public Subtitle() + { + _paragraphs = new List(); + _history = new List(); + FileName = "Untitled"; + } + + public Subtitle(List historyItems) + : this() + { + _history = historyItems; + } + + /// + /// Copy constructor (only paragraphs) + /// + /// Subtitle to copy + public Subtitle(Subtitle subtitle) + : this() + { + if (subtitle == null) + return; + + foreach (Paragraph p in subtitle.Paragraphs) + { + _paragraphs.Add(new Paragraph(p, false)); + } + _wasLoadedWithFrameNumbers = subtitle.WasLoadedWithFrameNumbers; + Header = subtitle.Header; + Footer = subtitle.Footer; + } + + public List Paragraphs + { + get + { + return _paragraphs; + } + } + + /// + /// Get the paragraph of index, null if out of bounds + /// + /// Index of wanted paragraph + /// Paragraph, null if index is index is out of bounds + public Paragraph GetParagraphOrDefault(int index) + { + if (_paragraphs == null || _paragraphs.Count <= index || index < 0) + return null; + + return _paragraphs[index]; + } + + public Paragraph GetParagraphOrDefaultById(string id) + { + foreach (Paragraph p in _paragraphs) + { + if (p.ID == id) + return p; + } + return null; + } + + public SubtitleFormat ReloadLoadSubtitle(List lines, string fileName) + { + Paragraphs.Clear(); + foreach (SubtitleFormat subtitleFormat in SubtitleFormat.AllSubtitleFormats) + { + if (subtitleFormat.IsMine(lines, fileName)) + { + subtitleFormat.LoadSubtitle(this, lines, fileName); + _format = subtitleFormat; + return subtitleFormat; + } + } + return null; + } + + public SubtitleFormat LoadSubtitle(string fileName, out Encoding encoding, Encoding useThisEncoding) + { + return LoadSubtitle(fileName, out encoding, useThisEncoding, false); + } + + public SubtitleFormat LoadSubtitle(string fileName, out Encoding encoding, Encoding useThisEncoding, bool batchMode) + { + FileName = fileName; + + _paragraphs = new List(); + + var lines = new List(); + StreamReader sr; + if (useThisEncoding != null) + { + try + { + sr = new StreamReader(fileName, useThisEncoding); + } + catch (Exception exception) + { + MessageBox.Show(exception.Message); + encoding = Encoding.UTF8; + return null; + } + } + else + { + try + { + sr = new StreamReader(fileName, Utilities.GetEncodingFromFile(fileName), true); + } + catch + { + try + { + Stream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + sr = new StreamReader(fs); + } + catch (Exception exception) + { + MessageBox.Show(exception.Message); + encoding = Encoding.UTF8; + return null; + } + } + } + + encoding = sr.CurrentEncoding; + while (!sr.EndOfStream) + lines.Add(sr.ReadLine()); + sr.Close(); + + foreach (SubtitleFormat subtitleFormat in SubtitleFormat.AllSubtitleFormats) + { + if (subtitleFormat.IsMine(lines, fileName)) + { + Header = null; + subtitleFormat.BatchMode = batchMode; + subtitleFormat.LoadSubtitle(this, lines, fileName); + _format = subtitleFormat; + _wasLoadedWithFrameNumbers = _format.IsFrameBased; + if (_wasLoadedWithFrameNumbers) + CalculateTimeCodesFromFrameNumbers(Configuration.Settings.General.CurrentFrameRate); + return subtitleFormat; + } + } + + if (useThisEncoding == null) + return LoadSubtitle(fileName, out encoding, Encoding.Unicode); + + return null; + } + + public void MakeHistoryForUndo(string description, SubtitleFormat subtitleFormat, DateTime fileModified, Subtitle original, string originalSubtitleFileName, int lineNumber, int linePosition, int linePositionAlternate) + { + // don't fill memory with history - use a max rollback points + if (_history.Count > MaximumHistoryItems) + _history.RemoveAt(0); + + _history.Add(new HistoryItem(_history.Count, this, description, FileName, fileModified, subtitleFormat.FriendlyName, original, originalSubtitleFileName, lineNumber, linePosition, linePositionAlternate)); + } + + public bool CanUndo + { + get + { + return _history.Count > 0; + } + } + + public string UndoHistory(int index, out string subtitleFormatFriendlyName, out DateTime fileModified, out Subtitle originalSubtitle, out string originalSubtitleFileName) + { + _paragraphs.Clear(); + foreach (Paragraph p in _history[index].Subtitle.Paragraphs) + _paragraphs.Add(new Paragraph(p)); + + subtitleFormatFriendlyName = _history[index].SubtitleFormatFriendlyName; + FileName = _history[index].FileName; + fileModified = _history[index].FileModified; + originalSubtitle = new Subtitle(_history[index].OriginalSubtitle); + originalSubtitleFileName = _history[index].OriginalSubtitleFileName; + + return FileName; + } + + /// + /// Creates subtitle as text in it'snative format + /// + /// Format to output + /// Native format as text string + public string ToText(SubtitleFormat format) + { + return format.ToText(this, Path.GetFileNameWithoutExtension(FileName)); + } + + public void AddTimeToAllParagraphs(TimeSpan time) + { + foreach (Paragraph p in Paragraphs) + { + p.StartTime.AddTime(time); + p.EndTime.AddTime(time); + } + } + + /// + /// Calculate the time codes from frame number/frame rate + /// + /// Number of frames per second + /// True if times could be calculated + public bool CalculateTimeCodesFromFrameNumbers(double frameRate) + { + if (_format == null || _format.IsTimeBased) + return false; + + foreach (Paragraph p in Paragraphs) + { + p.CalculateTimeCodesFromFrameNumbers(frameRate); + } + return true; + } + + /// + /// Calculate the frame numbers from time codes/frame rate + /// + /// + /// + public bool CalculateFrameNumbersFromTimeCodes(double frameRate) + { + if (_format == null || _format.IsFrameBased) + return false; + + foreach (Paragraph p in Paragraphs) + { + p.CalculateFrameNumbersFromTimeCodes(frameRate); + } + + FixEqualOrJustOverlappingFrameNumbers(); + + return true; + } + + public void CalculateFrameNumbersFromTimeCodesNoCheck(double frameRate) + { + foreach (Paragraph p in Paragraphs) + p.CalculateFrameNumbersFromTimeCodes(frameRate); + + FixEqualOrJustOverlappingFrameNumbers(); + } + + private void FixEqualOrJustOverlappingFrameNumbers() + { + for (int i = 0; i < Paragraphs.Count - 1; i++) + { + Paragraph p = Paragraphs[i]; + Paragraph next = GetParagraphOrDefault(i + 1); + if (next != null && (p.EndFrame == next.StartFrame || p.EndFrame == next.StartFrame + 1)) + p.EndFrame = next.StartFrame - 1; + } + } + + public void ChangeFrameRate(double oldFrameRate, double newFrameRate) + { + foreach (Paragraph p in Paragraphs) + { + double startFrame = p.StartTime.TotalMilliseconds / TimeCode.BaseUnit * oldFrameRate; + double endFrame = p.EndTime.TotalMilliseconds / TimeCode.BaseUnit * oldFrameRate; + p.StartTime.TotalMilliseconds = startFrame * (TimeCode.BaseUnit / newFrameRate); + p.EndTime.TotalMilliseconds = endFrame * (TimeCode.BaseUnit / newFrameRate); + p.CalculateFrameNumbersFromTimeCodes(newFrameRate); + } + } + + public bool WasLoadedWithFrameNumbers + { + get + { + return _wasLoadedWithFrameNumbers; + } + set + { + _wasLoadedWithFrameNumbers = value; + } + } + + public void AdjustDisplayTimeUsingPercent(double percent, ListView.SelectedIndexCollection selectedIndexes) + { + for (int i = 0; i < _paragraphs.Count; i++) + { + if (selectedIndexes == null || selectedIndexes.Contains(i)) + { + double nextStartMilliseconds = _paragraphs[_paragraphs.Count - 1].EndTime.TotalMilliseconds + TimeCode.BaseUnit; + if (i + 1 < _paragraphs.Count) + nextStartMilliseconds = _paragraphs[i + 1].StartTime.TotalMilliseconds; + + double newEndMilliseconds = _paragraphs[i].EndTime.TotalMilliseconds; + newEndMilliseconds = _paragraphs[i].StartTime.TotalMilliseconds + (((newEndMilliseconds - _paragraphs[i].StartTime.TotalMilliseconds) * percent) / 100); + if (newEndMilliseconds > nextStartMilliseconds) + newEndMilliseconds = nextStartMilliseconds - 1; + _paragraphs[i].EndTime.TotalMilliseconds = newEndMilliseconds; + } + } + } + + public void AdjustDisplayTimeUsingSeconds(double seconds, ListView.SelectedIndexCollection selectedIndexes) + { + for (int i = 0; i < _paragraphs.Count; i++) + { + if (selectedIndexes == null || selectedIndexes.Contains(i)) + { + double nextStartMilliseconds = _paragraphs[_paragraphs.Count - 1].EndTime.TotalMilliseconds + TimeCode.BaseUnit; + if (i + 1 < _paragraphs.Count) + nextStartMilliseconds = _paragraphs[i + 1].StartTime.TotalMilliseconds; + + double newEndMilliseconds = _paragraphs[i].EndTime.TotalMilliseconds + (seconds * TimeCode.BaseUnit); + if (newEndMilliseconds > nextStartMilliseconds) + newEndMilliseconds = nextStartMilliseconds - 1; + + if (seconds < 0) + { + if (_paragraphs[i].StartTime.TotalMilliseconds + 100 > newEndMilliseconds) + _paragraphs[i].EndTime.TotalMilliseconds = _paragraphs[i].StartTime.TotalMilliseconds + 100; + else + _paragraphs[i].EndTime.TotalMilliseconds = newEndMilliseconds; + } + else + { + _paragraphs[i].EndTime.TotalMilliseconds = newEndMilliseconds; + } + } + } + } + + public void RecalculateDisplayTimes(double maxCharactersPerSecond, ListView.SelectedIndexCollection selectedIndexes) + { + for (int i = 0; i < _paragraphs.Count; i++) + { + if (selectedIndexes == null || selectedIndexes.Contains(i)) + { + Paragraph p = _paragraphs[i]; + double duration = Utilities.GetOptimalDisplayMilliseconds(p.Text); + p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + duration; + while (Utilities.GetCharactersPerSecond(p) > maxCharactersPerSecond) + { + duration++; + p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + duration; + } + + Paragraph next = GetParagraphOrDefault(i + 1); + if (next != null && p.StartTime.TotalMilliseconds + duration + Configuration.Settings.General.MinimumMillisecondsBetweenLines > next.StartTime.TotalMilliseconds) + { + p.EndTime.TotalMilliseconds = next.StartTime.TotalMilliseconds - Configuration.Settings.General.MinimumMillisecondsBetweenLines; + if (p.Duration.TotalMilliseconds <= 0) + p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + 1; + } + } + } + } + + public void Renumber(int startNumber = 1) + { + foreach (Paragraph p in _paragraphs) + { + p.Number = startNumber++; + } + } + + public int GetIndex(Paragraph p) + { + if (p == null) + return -1; + + int index = _paragraphs.IndexOf(p); + if (index >= 0) + return index; + + for (int i = 0; i < _paragraphs.Count; i++) + { + if (p.ID == _paragraphs[i].ID) + return i; + if (i < _paragraphs.Count - 1 && p.ID == _paragraphs[i + 1].ID) + return i + 1; + if (p.StartTime.TotalMilliseconds == _paragraphs[i].StartTime.TotalMilliseconds && + p.EndTime.TotalMilliseconds == _paragraphs[i].EndTime.TotalMilliseconds) + return i; + if (p.Number == _paragraphs[i].Number && (p.StartTime.TotalMilliseconds == _paragraphs[i].StartTime.TotalMilliseconds || + p.EndTime.TotalMilliseconds == _paragraphs[i].EndTime.TotalMilliseconds)) + return i; + if (p.Text == _paragraphs[i].Text && (p.StartTime.TotalMilliseconds == _paragraphs[i].StartTime.TotalMilliseconds || + p.EndTime.TotalMilliseconds == _paragraphs[i].EndTime.TotalMilliseconds)) + return i; + } + return -1; + } + + public Paragraph GetFirstAlike(Paragraph p) + { + foreach (Paragraph item in _paragraphs) + { + if (p.StartTime.TotalMilliseconds == item.StartTime.TotalMilliseconds && + p.EndTime.TotalMilliseconds == item.EndTime.TotalMilliseconds && + p.Text == item.Text) + return item; + } + return null; + } + + public Paragraph GetFirstParagraphByLineNumber(int number) + { + foreach (Paragraph p in _paragraphs) + { + if (p.Number == number) + return p; + } + return null; + } + + public int RemoveEmptyLines() + { + int count = _paragraphs.Count; + if (count > 0) + { + int firstNumber = _paragraphs[0].Number; + for (int i = _paragraphs.Count - 1; i >= 0; i--) + { + Paragraph p = _paragraphs[i]; + if (string.IsNullOrWhiteSpace(p.Text)) + _paragraphs.RemoveAt(i); + } + if (count != _paragraphs.Count) + Renumber(firstNumber); + } + return count - _paragraphs.Count; + } + + /// + /// Sort subtitle paragraphs + /// + /// Paragraph sort criteria + public void Sort(SubtitleSortCriteria sortCriteria) + { + switch (sortCriteria) + { + case SubtitleSortCriteria.Number: + _paragraphs.Sort((p1, p2) => p1.Number.CompareTo(p2.Number)); + break; + case SubtitleSortCriteria.StartTime: + _paragraphs.Sort((p1, p2) => p1.StartTime.TotalMilliseconds.CompareTo(p2.StartTime.TotalMilliseconds)); + break; + case SubtitleSortCriteria.EndTime: + _paragraphs.Sort((p1, p2) => p1.EndTime.TotalMilliseconds.CompareTo(p2.EndTime.TotalMilliseconds)); + break; + case SubtitleSortCriteria.Duration: + _paragraphs.Sort((p1, p2) => p1.Duration.TotalMilliseconds.CompareTo(p2.Duration.TotalMilliseconds)); + break; + case SubtitleSortCriteria.Text: + _paragraphs.Sort((p1, p2) => string.Compare(p1.Text, p2.Text, StringComparison.Ordinal)); + break; + case SubtitleSortCriteria.TextMaxLineLength: + _paragraphs.Sort((p1, p2) => Utilities.GetMaxLineLength(p1.Text).CompareTo(Utilities.GetMaxLineLength(p2.Text))); + break; + case SubtitleSortCriteria.TextTotalLength: + _paragraphs.Sort((p1, p2) => p1.Text.Length.CompareTo(p2.Text.Length)); + break; + case SubtitleSortCriteria.TextNumberOfLines: + _paragraphs.Sort((p1, p2) => p1.NumberOfLines.CompareTo(p2.NumberOfLines)); + break; + case SubtitleSortCriteria.TextCharactersPerSeconds: + _paragraphs.Sort((p1, p2) => Utilities.GetCharactersPerSecond(p1).CompareTo(Utilities.GetCharactersPerSecond(p2))); + break; + case SubtitleSortCriteria.WordsPerMinute: + _paragraphs.Sort((p1, p2) => p1.WordsPerMinute.CompareTo(p2.WordsPerMinute)); + break; + case SubtitleSortCriteria.Style: + _paragraphs.Sort((p1, p2) => string.Compare(p1.Extra, p2.Extra, StringComparison.Ordinal)); + break; + } + } + + public void InsertParagraphInCorrectTimeOrder(Paragraph newParagraph) + { + for (int i = 0; i < Paragraphs.Count; i++) + { + Paragraph p = Paragraphs[i]; + if (newParagraph.StartTime.TotalMilliseconds < p.StartTime.TotalMilliseconds) + { + Paragraphs.Insert(i, newParagraph); + return; + } + } + Paragraphs.Add(newParagraph); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/AQTitle.cs b/libse/SubtitleFormats/AQTitle.cs new file mode 100644 index 000000000..1037b9916 --- /dev/null +++ b/libse/SubtitleFormats/AQTitle.cs @@ -0,0 +1,149 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class AQTitle : SubtitleFormat + { + private enum ExpectingLine + { + TimeStart, + Text, + TimeEndOrText, + } + + public override string Extension + { + get { return ".aqt"; } + } + + public override string Name + { + get { return "AQTitle"; } + } + + public override bool IsTimeBased + { + get { return false; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + //-->> 072058 + //Meine Mutter und meine Schwester, + + //-->> 072169 + + //-->> 072172 + //die in Zürich lebt, und ich, + + //-->> 072247 + const string paragraphWriteFormat = "-->> {0}{3}{2}{3}-->> {1}{3}{3}"; + + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + string text = Utilities.RemoveSsaTags(p.Text); + int noOfLines = Utilities.GetNumberOfLines(text); + if (noOfLines > 2) + text = Utilities.AutoBreakLine(text); + else if (noOfLines == 1) + text += Environment.NewLine; + + sb.AppendLine(string.Format(paragraphWriteFormat, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), text, Environment.NewLine)); + } + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + var paragraph = new Paragraph(); + var expecting = ExpectingLine.TimeStart; + _errorCount = 0; + + subtitle.Paragraphs.Clear(); + foreach (string line in lines) + { + if (line.StartsWith("-->> ", StringComparison.Ordinal)) + { + string timePart = line.Substring(4).Trim(); + if (timePart.Length > 0) + { + try + { + var tc = DecodeTimeCode(timePart); + if (expecting == ExpectingLine.TimeStart) + { + paragraph = new Paragraph { StartFrame = int.Parse(timePart), StartTime = tc }; + expecting = ExpectingLine.Text; + } + else if (expecting == ExpectingLine.TimeEndOrText) + { + paragraph.EndFrame = int.Parse(timePart); + paragraph.EndTime = tc; + subtitle.Paragraphs.Add(paragraph); + paragraph = new Paragraph(); + expecting = ExpectingLine.TimeStart; + } + } + catch + { + _errorCount++; + expecting = ExpectingLine.TimeStart; + } + } + } + else + { + if (expecting == ExpectingLine.Text || expecting == ExpectingLine.TimeEndOrText) + { + if (line.Length > 0) + { + string text = line.Replace("|", Environment.NewLine); + if (string.IsNullOrEmpty(paragraph.Text)) + paragraph.Text = text.Trim(); + else + paragraph.Text += Environment.NewLine + text; + if (paragraph.Text.Length > 2000) + { + _errorCount += 100; + return; + } + } + expecting = ExpectingLine.TimeEndOrText; + } + else if (expecting == ExpectingLine.TimeStart && !string.IsNullOrWhiteSpace(line)) + { + int ms = (int)paragraph.EndTime.TotalMilliseconds; + int frames = paragraph.EndFrame; + paragraph = new Paragraph { StartTime = { TotalMilliseconds = ms }, StartFrame = frames, Text = line.Trim() }; + expecting = ExpectingLine.TimeEndOrText; + } + } + } + subtitle.Renumber(); + } + + private static string EncodeTimeCode(TimeCode time) + { + int frames = MillisecondsToFrames(time.TotalMilliseconds) + 1; + return frames.ToString(CultureInfo.InvariantCulture); + } + + private static TimeCode DecodeTimeCode(string timePart) + { + int milliseconds = (int)((TimeCode.BaseUnit / Configuration.Settings.General.CurrentFrameRate) * int.Parse(timePart)); + return new TimeCode(milliseconds); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/AbcIViewer.cs b/libse/SubtitleFormats/AbcIViewer.cs new file mode 100644 index 000000000..4ec5ce09b --- /dev/null +++ b/libse/SubtitleFormats/AbcIViewer.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class AbcIViewer : SubtitleFormat + { + public override string Extension + { + get { return ".xml"; } + } + + public override string Name + { + get { return "ABC iView"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > 0; + } + + public override string ToText(Subtitle subtitle, string title) + { + string xmlStructure = + "" + Environment.NewLine + + "" + Environment.NewLine + + "" + Environment.NewLine + + "" + Environment.NewLine + + ""; + + var xml = new XmlDocument { XmlResolver = null }; + xml.LoadXml(xmlStructure); + XmlNode reel = xml.DocumentElement.SelectSingleNode("reel"); + foreach (Paragraph p in subtitle.Paragraphs) + { + XmlNode paragraph = xml.CreateElement("title"); + + XmlAttribute start = xml.CreateAttribute("start"); + start.InnerText = ToTimeCode(p.StartTime.TotalMilliseconds); + paragraph.Attributes.Append(start); + + XmlAttribute end = xml.CreateAttribute("end"); + end.InnerText = ToTimeCode(p.EndTime.TotalMilliseconds); + paragraph.Attributes.Append(end); + + paragraph.InnerText = HtmlUtil.RemoveHtmlTags(p.Text.Replace(Environment.NewLine, "|"), true); + + reel.AppendChild(paragraph); + } + + return ToUtf8XmlString(xml); + } + + private static string ToTimeCode(double totalMilliseconds) + { + var ts = TimeSpan.FromMilliseconds(totalMilliseconds); + return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + + string xmlString = sb.ToString(); + if (!xmlString.Contains(" lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > 0; + } + + public override string ToText(Subtitle subtitle, string title) + { + string xmlStructure = @" + + + 1 + + + + + +".Replace("'", "\""); + + var xml = new XmlDocument { XmlResolver = null }; + xml.LoadXml(xmlStructure); + const string innerXml = "", @"{\i}"); + text = text.Replace("", @"{\u1}"); + text = text.Replace("", @"{\u0}"); + text = text.Replace("", @"{\u}"); + text = text.Replace("", @"{\b1}"); + text = text.Replace("", @"{\b0}"); + text = text.Replace("", @"{\b}"); + int count = 0; + while (text.Contains("', start); + if (end > 0) + { + string fontTag = text.Substring(start + 5, end - (start + 4)); + text = text.Remove(start, end - start + 1); + int indexOfEndFont = text.IndexOf("", start, StringComparison.Ordinal); + if (indexOfEndFont > 0) + { + text = text.Remove(indexOfEndFont, 7); + if (indexOfEndFont < text.Length - 9) + text = text.Insert(indexOfEndFont, "{\\c}"); + } + + fontTag = FormatTag(ref text, start, fontTag, "face=\"", "fn", "}"); + fontTag = FormatTag(ref text, start, fontTag, "face='", "fn", "}"); + fontTag = FormatTag(ref text, start, fontTag, "face=", "fn", "}"); + + fontTag = FormatTag(ref text, start, fontTag, "size=\"", "fs", "}"); + fontTag = FormatTag(ref text, start, fontTag, "size='", "fs", "}"); + fontTag = FormatTag(ref text, start, fontTag, "size=", "fs", "}"); + + fontTag = FormatTag(ref text, start, fontTag, "color=\"", "c&H", "&}"); + fontTag = FormatTag(ref text, start, fontTag, "color='", "c&H", "&}"); + FormatTag(ref text, start, fontTag, "color=", "c&H", "&}"); + } + count++; + } + return text.Replace("{\\c}", "@___@@").Replace("}{", string.Empty).Replace("@___@@", "{\\c}").Replace("{\\c}{\\c&", "{\\c&"); + } + + private static string FormatTag(ref string text, int start, string fontTag, string tag, string ssaTagName, string endSsaTag) + { + if (fontTag.Contains(tag)) + { + int fontStart = fontTag.IndexOf(tag, StringComparison.Ordinal); + int fontEnd = fontTag.IndexOfAny(new[] { '"', '\'', ' ', '>' }, fontStart + tag.Length); + if (fontEnd > 0) + { + string subTag = fontTag.Substring(fontStart + tag.Length, fontEnd - (fontStart + tag.Length)); + if (tag.Contains("color")) + { + subTag = subTag.Replace("#", string.Empty); + + // switch from rrggbb to bbggrr + if (subTag.Length >= 6) + subTag = subTag.Remove(subTag.Length - 6) + subTag.Substring(subTag.Length - 2, 2) + subTag.Substring(subTag.Length - 4, 2) + subTag.Substring(subTag.Length - 6, 2); + } + fontTag = fontTag.Remove(fontStart, fontEnd - fontStart + 1); + if (start < text.Length) + text = text.Insert(start, @"{\" + ssaTagName + subTag + endSsaTag); + } + } + return fontTag; + } + + public static string GetFormattedText(string text) + { + text = text.Replace("\\N", Environment.NewLine).Replace("\\n", Environment.NewLine); + + for (int i = 0; i < 10; i++) // just look ten times... + { + bool italic; + if (text.Contains(@"{\fn")) + { + int start = text.IndexOf(@"{\fn", StringComparison.Ordinal); + int end = text.IndexOf('}', start); + if (end > 0 && !text.Substring(start).StartsWith("{\\fn}", StringComparison.Ordinal)) + { + string fontName = text.Substring(start + 4, end - (start + 4)); + string extraTags = string.Empty; + CheckAndAddSubTags(ref fontName, ref extraTags, out italic); + text = text.Remove(start, end - start + 1); + if (italic) + text = text.Insert(start, ""); + else + text = text.Insert(start, ""); + + int indexOfEndTag = text.IndexOf("{\\fn}", start, StringComparison.Ordinal); + if (indexOfEndTag > 0) + text = text.Remove(indexOfEndTag, "{\\fn}".Length).Insert(indexOfEndTag, ""); + else + text += ""; + } + } + + if (text.Contains(@"{\fs")) + { + int start = text.IndexOf(@"{\fs", StringComparison.Ordinal); + int end = text.IndexOf('}', start); + if (end > 0 && !text.Substring(start).StartsWith("{\\fs}", StringComparison.Ordinal)) + { + string fontSize = text.Substring(start + 4, end - (start + 4)); + string extraTags = string.Empty; + CheckAndAddSubTags(ref fontSize, ref extraTags, out italic); + if (Utilities.IsInteger(fontSize)) + { + text = text.Remove(start, end - start + 1); + if (italic) + text = text.Insert(start, ""); + else + text = text.Insert(start, ""); + + int indexOfEndTag = text.IndexOf("{\\fs}", start, StringComparison.Ordinal); + if (indexOfEndTag > 0) + text = text.Remove(indexOfEndTag, "{\\fs}".Length).Insert(indexOfEndTag, ""); + else + text += ""; + } + } + } + + if (text.Contains(@"{\c")) + { + int start = text.IndexOf(@"{\c", StringComparison.Ordinal); + int end = text.IndexOf('}', start); + if (end > 0 && !text.Substring(start).StartsWith("{\\c}", StringComparison.Ordinal) && !text.Substring(start).StartsWith("{\\clip", StringComparison.Ordinal)) + { + string color = text.Substring(start + 4, end - (start + 4)); + string extraTags = string.Empty; + CheckAndAddSubTags(ref color, ref extraTags, out italic); + + color = color.Replace("&", string.Empty).TrimStart('H'); + color = color.PadLeft(6, '0'); + + // switch to rrggbb from bbggrr + color = "#" + color.Remove(color.Length - 6) + color.Substring(color.Length - 2, 2) + color.Substring(color.Length - 4, 2) + color.Substring(color.Length - 6, 2); + color = color.ToLower(); + + text = text.Remove(start, end - start + 1); + if (italic) + text = text.Insert(start, ""); + else + text = text.Insert(start, ""); + int indexOfEndTag = text.IndexOf("{\\c}", start, StringComparison.Ordinal); + int indexOfNextColorTag = text.IndexOf("{\\c&", start, StringComparison.Ordinal); + if (indexOfNextColorTag > 0 && (indexOfNextColorTag < indexOfEndTag || indexOfEndTag == -1)) + text = text.Insert(indexOfNextColorTag, ""); + else if (indexOfEndTag > 0) + text = text.Remove(indexOfEndTag, "{\\c}".Length).Insert(indexOfEndTag, ""); + else + text += ""; + } + } + + if (text.Contains(@"{\1c")) // "1" specifices primary color + { + int start = text.IndexOf(@"{\1c", StringComparison.Ordinal); + int end = text.IndexOf('}', start); + if (end > 0 && !text.Substring(start).StartsWith("{\\1c}", StringComparison.Ordinal)) + { + string color = text.Substring(start + 5, end - (start + 5)); + string extraTags = string.Empty; + CheckAndAddSubTags(ref color, ref extraTags, out italic); + + color = color.Replace("&", string.Empty).TrimStart('H'); + color = color.PadLeft(6, '0'); + + // switch to rrggbb from bbggrr + color = "#" + color.Remove(color.Length - 6) + color.Substring(color.Length - 2, 2) + color.Substring(color.Length - 4, 2) + color.Substring(color.Length - 6, 2); + color = color.ToLower(); + + text = text.Remove(start, end - start + 1); + if (italic) + text = text.Insert(start, ""); + else + text = text.Insert(start, ""); + text += ""; + } + } + } + + text = text.Replace(@"{\i1}", ""); + text = text.Replace(@"{\i0}", ""); + text = text.Replace(@"{\i}", ""); + if (Utilities.CountTagInText(text, "") > Utilities.CountTagInText(text, "")) + text += ""; + + text = text.Replace(@"{\u1}", ""); + text = text.Replace(@"{\u0}", ""); + text = text.Replace(@"{\u}", ""); + if (Utilities.CountTagInText(text, "") > Utilities.CountTagInText(text, "")) + text += ""; + + text = text.Replace(@"{\b1}", ""); + text = text.Replace(@"{\b0}", ""); + text = text.Replace(@"{\b}", ""); + if (Utilities.CountTagInText(text, "") > Utilities.CountTagInText(text, "")) + text += ""; + + return text; + } + + private static void CheckAndAddSubTags(ref string tagName, ref string extraTags, out bool italic) + { + italic = false; + int indexOfSPlit = tagName.IndexOf('\\'); + if (indexOfSPlit > 0) + { + string rest = tagName.Substring(indexOfSPlit).TrimStart('\\'); + tagName = tagName.Remove(indexOfSPlit); + + for (int i = 0; i < 10; i++) + { + if (rest.StartsWith("fs", StringComparison.Ordinal) && rest.Length > 2) + { + indexOfSPlit = rest.IndexOf('\\'); + string fontSize = rest; + if (indexOfSPlit > 0) + { + fontSize = rest.Substring(0, indexOfSPlit); + rest = rest.Substring(indexOfSPlit).TrimStart('\\'); + } + else + { + rest = string.Empty; + } + extraTags += " size=\"" + fontSize.Substring(2) + "\""; + } + else if (rest.StartsWith("fn", StringComparison.Ordinal) && rest.Length > 2) + { + indexOfSPlit = rest.IndexOf('\\'); + string fontName = rest; + if (indexOfSPlit > 0) + { + fontName = rest.Substring(0, indexOfSPlit); + rest = rest.Substring(indexOfSPlit).TrimStart('\\'); + } + else + { + rest = string.Empty; + } + extraTags += " face=\"" + fontName.Substring(2) + "\""; + } + else if (rest.StartsWith('c') && rest.Length > 2) + { + indexOfSPlit = rest.IndexOf('\\'); + string fontColor = rest; + if (indexOfSPlit > 0) + { + fontColor = rest.Substring(0, indexOfSPlit); + rest = rest.Substring(indexOfSPlit).TrimStart('\\'); + } + else + { + rest = string.Empty; + } + + string color = fontColor.Substring(2); + color = color.Replace("&", string.Empty).TrimStart('H'); + color = color.PadLeft(6, '0'); + // switch to rrggbb from bbggrr + color = "#" + color.Remove(color.Length - 6) + color.Substring(color.Length - 2, 2) + color.Substring(color.Length - 4, 2) + color.Substring(color.Length - 6, 2); + color = color.ToLower(); + + extraTags += " color=\"" + color + "\""; + } + else if (rest.StartsWith("i1", StringComparison.Ordinal) && rest.Length > 1) + { + indexOfSPlit = rest.IndexOf('\\'); + italic = true; + if (indexOfSPlit > 0) + { + rest = rest.Substring(indexOfSPlit).TrimStart('\\'); + } + else + { + rest = string.Empty; + } + } + else if (rest.Length > 0 && rest.Contains("\\")) + { + indexOfSPlit = rest.IndexOf('\\'); + rest = rest.Substring(indexOfSPlit).TrimStart('\\'); + } + } + } + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + Errors = null; + bool eventsStarted = false; + bool fontsStarted = false; + bool graphicsStarted = false; + subtitle.Paragraphs.Clear(); + string[] format = "Layer, Start, End, Style, Actor, MarginL, MarginR, MarginV, Effect, Text".Split(','); + int indexLayer = 0; + int indexStart = 1; + int indexEnd = 2; + int indexStyle = 3; + int indexActor = 4; + int indexEffect = 8; + int indexText = 9; + var errors = new StringBuilder(); + int lineNumber = 0; + + var header = new StringBuilder(); + var footer = new StringBuilder(); + foreach (string line in lines) + { + lineNumber++; + if (!eventsStarted && !fontsStarted && !graphicsStarted) + header.AppendLine(line); + + if (string.IsNullOrWhiteSpace(line) || line.TrimStart().StartsWith(';')) + { + // skip empty and comment lines + } + else if (line.TrimStart().StartsWith("dialog:", StringComparison.OrdinalIgnoreCase) || line.TrimStart().StartsWith("dialogue:", StringComparison.OrdinalIgnoreCase)) // fix faulty font tags... + { + eventsStarted = true; + fontsStarted = false; + graphicsStarted = false; + } + + if (line.Trim().Equals("[events]", StringComparison.OrdinalIgnoreCase)) + { + eventsStarted = true; + fontsStarted = false; + graphicsStarted = false; + } + else if (line.Trim().Equals("[fonts]", StringComparison.OrdinalIgnoreCase)) + { + eventsStarted = false; + fontsStarted = true; + graphicsStarted = false; + footer.AppendLine(); + footer.AppendLine("[Fonts]"); + } + else if (line.Trim().Equals("[graphics]", StringComparison.OrdinalIgnoreCase)) + { + eventsStarted = false; + fontsStarted = false; + graphicsStarted = true; + footer.AppendLine(); + footer.AppendLine("[Graphics]"); + } + else if (fontsStarted) + { + footer.AppendLine(line); + } + else if (graphicsStarted) + { + footer.AppendLine(line); + } + else if (eventsStarted) + { + string s = line.Trim().ToLower(); + if (s.StartsWith("format:", StringComparison.Ordinal)) + { + if (line.Length > 10) + { + format = line.ToLower().Substring(8).Split(','); + for (int i = 0; i < format.Length; i++) + { + if (format[i].Trim().Equals("start", StringComparison.OrdinalIgnoreCase)) + indexStart = i; + else if (format[i].Trim().Equals("end", StringComparison.OrdinalIgnoreCase)) + indexEnd = i; + else if (format[i].Trim().Equals("text", StringComparison.OrdinalIgnoreCase)) + indexText = i; + else if (format[i].Trim().Equals("style", StringComparison.OrdinalIgnoreCase)) + indexStyle = i; + else if (format[i].Trim().Equals("actor", StringComparison.OrdinalIgnoreCase)) + indexActor = i; + else if (format[i].Trim().Equals("effect", StringComparison.OrdinalIgnoreCase)) + indexEffect = i; + else if (format[i].Trim().Equals("layer", StringComparison.OrdinalIgnoreCase)) + indexLayer = i; + } + } + } + else if (!string.IsNullOrEmpty(s)) + { + var text = string.Empty; + var start = string.Empty; + var end = string.Empty; + var style = string.Empty; + var actor = string.Empty; + var effect = string.Empty; + var layer = 0; + + string[] splittedLine; + if (s.StartsWith("dialog:", StringComparison.Ordinal)) + splittedLine = line.Remove(0, 7).Split(','); + else if (s.StartsWith("dialogue:", StringComparison.Ordinal)) + splittedLine = line.Remove(0, 9).Split(','); + else + splittedLine = line.Split(','); + + for (int i = 0; i < splittedLine.Length; i++) + { + if (i == indexStart) + start = splittedLine[i].Trim(); + else if (i == indexEnd) + end = splittedLine[i].Trim(); + else if (i == indexStyle) + style = splittedLine[i].Trim(); + else if (i == indexActor) + actor = splittedLine[i].Trim(); + else if (i == indexEffect) + effect = splittedLine[i].Trim(); + else if (i == indexLayer) + int.TryParse(splittedLine[i].Trim(), out layer); + else if (i == indexText) + text = splittedLine[i]; + else if (i > indexText) + text += "," + splittedLine[i]; + } + + try + { + var p = new Paragraph + { + StartTime = GetTimeCodeFromString(start), + EndTime = GetTimeCodeFromString(end), + Text = GetFormattedText(text) + }; + + if (!string.IsNullOrEmpty(style)) + p.Extra = style; + if (!string.IsNullOrEmpty(actor)) + p.Actor = actor; + if (!string.IsNullOrEmpty(effect)) + p.Effect = effect; + p.Layer = layer; + p.IsComment = s.StartsWith("comment:", StringComparison.Ordinal); + subtitle.Paragraphs.Add(p); + } + catch + { + _errorCount++; + if (errors.Length < 2000) + errors.AppendLine(string.Format(Configuration.Settings.Language.Main.LineNumberXErrorReadingTimeCodeFromSourceLineY, lineNumber, line)); + } + } + } + } + if (header.Length > 0) + subtitle.Header = header.ToString(); + if (footer.Length > 0) + subtitle.Footer = footer.ToString().Trim(); + subtitle.Renumber(); + Errors = errors.ToString(); + } + + private static TimeCode GetTimeCodeFromString(string time) + { + // h:mm:ss.cc + string[] timeCode = time.Split(':', '.'); + return new TimeCode(int.Parse(timeCode[0]), + int.Parse(timeCode[1]), + int.Parse(timeCode[2]), + int.Parse(timeCode[3]) * 10); + } + + public override void RemoveNativeFormatting(Subtitle subtitle, SubtitleFormat newFormat) + { + if (newFormat != null && newFormat.Name == SubStationAlpha.NameOfFormat) + { + foreach (Paragraph p in subtitle.Paragraphs) + { + string s = p.Text; + if (s.Contains('{') && s.Contains('}')) + { + s = s.Replace(@"\u0", string.Empty); + s = s.Replace(@"\u1", string.Empty); + s = s.Replace(@"\s0", string.Empty); + s = s.Replace(@"\s1", string.Empty); + s = s.Replace(@"\be0", string.Empty); + s = s.Replace(@"\be1", string.Empty); + + s = RemoveTag(s, "shad"); + s = RemoveTag(s, "fsc"); + s = RemoveTag(s, "fsp"); + s = RemoveTag(s, "fr"); + + s = RemoveTag(s, "t("); + s = RemoveTag(s, "move("); + s = RemoveTag(s, "Position("); + s = RemoveTag(s, "org("); + s = RemoveTag(s, "fade("); + s = RemoveTag(s, "fad("); + s = RemoveTag(s, "clip("); + s = RemoveTag(s, "pbo("); + + // TODO: Alignment tags + + s = s.Replace("{}", string.Empty); + + p.Text = s; + } + } + } + else + { + foreach (Paragraph p in subtitle.Paragraphs) + { + int indexOfBegin = p.Text.IndexOf('{'); + string pre = string.Empty; + while (indexOfBegin >= 0 && p.Text.IndexOf('}') > indexOfBegin) + { + string s = p.Text.Substring(indexOfBegin); + if (s.StartsWith("{\\an1}", StringComparison.Ordinal) || + s.StartsWith("{\\an2}", StringComparison.Ordinal) || + s.StartsWith("{\\an3}", StringComparison.Ordinal) || + s.StartsWith("{\\an4}", StringComparison.Ordinal) || + s.StartsWith("{\\an5}", StringComparison.Ordinal) || + s.StartsWith("{\\an6}", StringComparison.Ordinal) || + s.StartsWith("{\\an7}", StringComparison.Ordinal) || + s.StartsWith("{\\an8}", StringComparison.Ordinal) || + s.StartsWith("{\\an9}", StringComparison.Ordinal)) + { + pre = s.Substring(0, 6); + } + else if (s.StartsWith("{\\an1\\", StringComparison.Ordinal) || + s.StartsWith("{\\an2\\", StringComparison.Ordinal) || + s.StartsWith("{\\an3\\", StringComparison.Ordinal) || + s.StartsWith("{\\an4\\", StringComparison.Ordinal) || + s.StartsWith("{\\an5\\", StringComparison.Ordinal) || + s.StartsWith("{\\an6\\", StringComparison.Ordinal) || + s.StartsWith("{\\an7\\", StringComparison.Ordinal) || + s.StartsWith("{\\an8\\", StringComparison.Ordinal) || + s.StartsWith("{\\an9\\", StringComparison.Ordinal)) + { + pre = s.Substring(0, 5) + "}"; + } + int indexOfEnd = p.Text.IndexOf('}'); + p.Text = p.Text.Remove(indexOfBegin, (indexOfEnd - indexOfBegin) + 1); + + indexOfBegin = p.Text.IndexOf('{'); + } + p.Text = pre + p.Text; + } + } + } + + private static string RemoveTag(string s, string tag) + { + int indexOfTag = s.IndexOf(@"\" + tag, StringComparison.Ordinal); + if (indexOfTag > 0) + { + var endIndex1 = s.IndexOf('\\', indexOfTag + 1); + var endIndex2 = s.IndexOf('}', indexOfTag + 1); + endIndex1 = Math.Min(endIndex1, endIndex2); + if (endIndex1 > 0) + s = s.Remove(indexOfTag, endIndex1 - indexOfTag); + } + return s; + } + + /// + /// BGR color like this: &HBBGGRR& (where BB, GG, and RR are hex values in uppercase) + /// + /// Input string + /// Default color + /// Input string as color, or default color if problems + public static Color GetSsaColor(string f, Color defaultColor) + { + //Red = &H0000FF& + //Green = &H00FF00& + //Blue = &HFF0000& + //White = &HFFFFFF& + //Black = &H000000& + string s = f.Trim().Trim('&'); + + if (s.StartsWith('h') && s.Length < 7) + { + while (s.Length < 7) + s = s.Insert(1, "0"); + } + + if (s.StartsWith('h') && s.Length == 7) + { + s = s.Substring(1); + string hexColor = "#" + s.Substring(4, 2) + s.Substring(2, 2) + s.Substring(0, 2); + try + { + return ColorTranslator.FromHtml(hexColor); + } + catch + { + return defaultColor; + } + } + if (s.StartsWith('h') && s.Length == 9) + { + s = s.Substring(3); + string hexColor = "#" + s.Substring(4, 2) + s.Substring(2, 2) + s.Substring(0, 2); + try + { + var c = ColorTranslator.FromHtml(hexColor); + + return c; + } + catch + { + return defaultColor; + } + } + int number; + if (int.TryParse(f, out number)) + { + Color temp = Color.FromArgb(number); + return Color.FromArgb(255, temp.B, temp.G, temp.R); + } + return defaultColor; + } + + public static string GetSsaColorString(Color c) + { + return string.Format("&H00{0:X2}{1:X2}{2:X2}", c.B, c.G, c.R); + } + + public static string CheckForErrors(string header) + { + if (string.IsNullOrEmpty(header)) + return string.Empty; + + var sb = new StringBuilder(); + + int styleCount = -1; + + int nameIndex = -1; + int fontNameIndex = -1; + int fontsizeIndex = -1; + int primaryColourIndex = -1; + int secondaryColourIndex = -1; + int outlineColourIndex = -1; + int backColourIndex = -1; + int boldIndex = -1; + int italicIndex = -1; + int underlineIndex = -1; + int outlineIndex = -1; + int shadowIndex = -1; + int alignmentIndex = -1; + int marginLIndex = -1; + int marginRIndex = -1; + int marginVIndex = -1; + int borderStyleIndex = -1; + + foreach (string line in header.SplitToLines()) + { + string s = line.Trim().ToLower(); + if (s.StartsWith("format:", StringComparison.Ordinal)) + { + if (line.Length > 10) + { + var format = line.Substring(8).ToLower().Split(','); + styleCount = format.Length; + for (int i = 0; i < format.Length; i++) + { + string f = format[i].Trim(); + if (f == "name") + nameIndex = i; + else if (f == "fontname") + fontNameIndex = i; + else if (f == "fontsize") + fontsizeIndex = i; + else if (f == "primarycolour") + primaryColourIndex = i; + else if (f == "secondarycolour") + secondaryColourIndex = i; + else if (f == "outlinecolour") + outlineColourIndex = i; + else if (f == "backcolour") + backColourIndex = i; + else if (f == "bold") + boldIndex = i; + else if (f == "italic") + italicIndex = i; + else if (f == "underline") + underlineIndex = i; + else if (f == "outline") + outlineIndex = i; + else if (f == "shadow") + shadowIndex = i; + else if (f == "alignment") + alignmentIndex = i; + else if (f == "marginl") + marginLIndex = i; + else if (f == "marginr") + marginRIndex = i; + else if (f == "marginv") + marginVIndex = i; + else if (f == "borderstyle") + borderStyleIndex = i; + } + } + } + else if (s.Replace(" ", string.Empty).StartsWith("style:", StringComparison.Ordinal)) + { + if (line.Length > 10) + { + string rawLine = line; + var format = line.Substring(6).Split(','); + + if (format.Length != styleCount) + { + sb.AppendLine("Number of expected Style elements do not match number of Format elements: " + rawLine); + sb.AppendLine(); + } + else + { + Color dummyColor = Color.FromArgb(9, 14, 16, 26); + for (int i = 0; i < format.Length; i++) + { + string f = format[i].Trim().ToLower(); + if (i == nameIndex) + { + if (f.Length == 0) + { + sb.AppendLine("'Name' is empty: " + rawLine); + sb.AppendLine(); + } + } + else if (i == fontNameIndex) + { + if (f.Length == 0) + { + sb.AppendLine("'Fontname' is empty: " + rawLine); + sb.AppendLine(); + } + } + else if (i == fontsizeIndex) + { + int number; + if (!int.TryParse(f, out number) || f.StartsWith('-')) + { + sb.AppendLine("'Fontsize' incorrect: " + rawLine); + sb.AppendLine(); + } + } + else if (i == primaryColourIndex) + { + if (GetSsaColor(f, dummyColor) == dummyColor || f == "&h") + { + sb.AppendLine("'PrimaryColour' incorrect: " + rawLine); + sb.AppendLine(); + } + } + else if (i == secondaryColourIndex) + { + if (GetSsaColor(f, dummyColor) == dummyColor) + { + sb.AppendLine("'SecondaryColour' incorrect: " + rawLine); + sb.AppendLine(); + } + } + else if (i == outlineColourIndex) + { + if (GetSsaColor(f, dummyColor) == dummyColor) + { + sb.AppendLine("'OutlineColour' incorrect: " + rawLine); + sb.AppendLine(); + } + } + else if (i == backColourIndex) + { + if (GetSsaColor(f, dummyColor) == dummyColor) + { + sb.AppendLine("'BackColour' incorrect: " + rawLine); + sb.AppendLine(); + } + } + else if (i == boldIndex) + { + if (Utilities.AllLetters.Contains(f)) + { + sb.AppendLine("'Bold' incorrect: " + rawLine); + sb.AppendLine(); + } + } + else if (i == italicIndex) + { + if (Utilities.AllLetters.Contains(f)) + { + sb.AppendLine("'Italic' incorrect: " + rawLine); + sb.AppendLine(); + } + } + else if (i == underlineIndex) + { + if (Utilities.AllLetters.Contains(f)) + { + sb.AppendLine("'Underline' incorrect: " + rawLine); + sb.AppendLine(); + } + } + else if (i == outlineIndex) + { + float number; + if (!float.TryParse(f, out number) || f.StartsWith('-')) + { + sb.AppendLine("'Outline' (width) incorrect: " + rawLine); + sb.AppendLine(); + } + } + else if (i == shadowIndex) + { + float number; + if (!float.TryParse(f, out number) || f.StartsWith('-')) + { + sb.AppendLine("'Shadow' (width) incorrect: " + rawLine); + sb.AppendLine(); + } + } + else if (i == alignmentIndex) + { + if (!"101123456789 ".Contains(f)) + { + sb.AppendLine("'Alignment' incorrect: " + rawLine); + sb.AppendLine(); + } + } + else if (i == marginLIndex) + { + int number; + if (!int.TryParse(f, out number) || f.StartsWith('-')) + { + sb.AppendLine("'MarginL' incorrect: " + rawLine); + sb.AppendLine(); + } + } + else if (i == marginRIndex) + { + int number; + if (!int.TryParse(f, out number) || f.StartsWith('-')) + { + sb.AppendLine("'MarginR' incorrect: " + rawLine); + sb.AppendLine(); + } + } + else if (i == marginVIndex) + { + int number; + if (!int.TryParse(f, out number) || f.StartsWith('-')) + { + sb.AppendLine("'MarginV' incorrect: " + rawLine); + sb.AppendLine(); + } + } + else if (i == borderStyleIndex) + { + if (f.Length != 0 && !"123".Contains(f)) + { + sb.AppendLine("'BorderStyle' incorrect: " + rawLine); + sb.AppendLine(); + } + } + } + } + } + } + } + return sb.ToString(); + } + + /// + /// Add new style to ASS header + /// + /// Header with new style + public static string AddSsaStyle(SsaStyle style, string header) + { + if (string.IsNullOrEmpty(header)) + header = DefaultHeader; + + var sb = new StringBuilder(); + bool stylesStarted = false; + bool styleAdded = false; + string styleFormat = "Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding"; + foreach (string line in header.SplitToLines()) + { + if (line.Equals("[V4+ Styles]", StringComparison.OrdinalIgnoreCase) || line.Equals("[V4 Styles]", StringComparison.OrdinalIgnoreCase)) + stylesStarted = true; + if (line.StartsWith("format:", StringComparison.OrdinalIgnoreCase)) + styleFormat = line; + if (!line.StartsWith("Style: " + style.Name + ",", StringComparison.Ordinal)) // overwrite existing style + sb.AppendLine(line); + if (!styleAdded && stylesStarted && line.TrimStart().StartsWith("style:", StringComparison.OrdinalIgnoreCase)) + { + sb.AppendLine(style.ToRawAss(styleFormat)); + styleAdded = true; + } + } + return sb.ToString(); + } + + public static SsaStyle GetSsaStyle(string styleName, string header) + { + var style = new SsaStyle { Name = styleName }; + + int nameIndex = -1; + int fontNameIndex = -1; + int fontsizeIndex = -1; + int primaryColourIndex = -1; + int secondaryColourIndex = -1; + int tertiaryColourIndex = -1; + int outlineColourIndex = -1; + int backColourIndex = -1; + int boldIndex = -1; + int italicIndex = -1; + int underlineIndex = -1; + int outlineIndex = -1; + int shadowIndex = -1; + int alignmentIndex = -1; + int marginLIndex = -1; + int marginRIndex = -1; + int marginVIndex = -1; + int borderStyleIndex = -1; + + if (header == null) + header = DefaultHeader; + + foreach (string line in header.SplitToLines()) + { + string s = line.Trim().ToLower(); + if (s.StartsWith("format:", StringComparison.Ordinal)) + { + if (line.Length > 10) + { + var format = line.ToLower().Substring(8).Split(','); + for (int i = 0; i < format.Length; i++) + { + string f = format[i].Trim().ToLower(); + if (f == "name") + nameIndex = i; + else if (f == "fontname") + fontNameIndex = i; + else if (f == "fontsize") + fontsizeIndex = i; + else if (f == "primarycolour") + primaryColourIndex = i; + else if (f == "secondarycolour") + secondaryColourIndex = i; + else if (f == "tertiarycolour") + tertiaryColourIndex = i; + else if (f == "outlinecolour") + outlineColourIndex = i; + else if (f == "backcolour") + backColourIndex = i; + else if (f == "bold") + boldIndex = i; + else if (f == "italic") + italicIndex = i; + else if (f == "underline") + underlineIndex = i; + else if (f == "outline") + outlineIndex = i; + else if (f == "shadow") + shadowIndex = i; + else if (f == "alignment") + alignmentIndex = i; + else if (f == "marginl") + marginLIndex = i; + else if (f == "marginr") + marginRIndex = i; + else if (f == "marginv") + marginVIndex = i; + else if (f == "borderstyle") + borderStyleIndex = i; + } + } + } + else if (s.Replace(" ", string.Empty).StartsWith("style:", StringComparison.Ordinal)) + { + if (line.Length > 10) + { + style.RawLine = line; + var format = line.Substring(6).Split(','); + for (int i = 0; i < format.Length; i++) + { + string f = format[i].Trim().ToLower(); + if (i == nameIndex) + { + style.Name = format[i].Trim(); + } + else if (i == fontNameIndex) + { + style.FontName = f; + } + else if (i == fontsizeIndex) + { + int number; + if (int.TryParse(f, out number)) + style.FontSize = number; + } + else if (i == primaryColourIndex) + { + style.Primary = GetSsaColor(f, Color.White); + } + else if (i == secondaryColourIndex) + { + style.Secondary = GetSsaColor(f, Color.Yellow); + } + else if (i == tertiaryColourIndex) + { + style.Tertiary = GetSsaColor(f, Color.Yellow); + } + else if (i == outlineColourIndex) + { + style.Outline = GetSsaColor(f, Color.Black); + } + else if (i == backColourIndex) + { + style.Background = GetSsaColor(f, Color.Black); + } + else if (i == boldIndex) + { + style.Bold = f == "1"; + } + else if (i == italicIndex) + { + style.Italic = f == "1"; + } + else if (i == underlineIndex) + { + style.Underline = f == "1"; + } + else if (i == outlineIndex) + { + int number; + if (int.TryParse(f, out number)) + style.OutlineWidth = number; + } + else if (i == shadowIndex) + { + int number; + if (int.TryParse(f, out number)) + style.ShadowWidth = number; + } + else if (i == alignmentIndex) + { + style.Alignment = f; + } + else if (i == marginLIndex) + { + int number; + if (int.TryParse(f, out number)) + style.MarginLeft = number; + } + else if (i == marginRIndex) + { + int number; + if (int.TryParse(f, out number)) + style.MarginRight = number; + } + else if (i == marginVIndex) + { + int number; + if (int.TryParse(f, out number)) + style.MarginVertical = number; + } + else if (i == borderStyleIndex) + { + style.BorderStyle = f; + } + } + } + if (styleName != null && style.Name != null && styleName.Equals(style.Name, StringComparison.OrdinalIgnoreCase)) + { + style.LoadedFromHeader = true; + return style; + } + } + } + return new SsaStyle { Name = styleName }; + } + + public override bool HasStyleSupport + { + get + { + return true; + } + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/AvidCaption.cs b/libse/SubtitleFormats/AvidCaption.cs new file mode 100644 index 000000000..0a11fc58b --- /dev/null +++ b/libse/SubtitleFormats/AvidCaption.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class AvidCaption : SubtitleFormat + { + private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d:\d\d:\d\d:\d\d \d\d:\d\d:\d\d:\d\d$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Avid Caption"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + int index = 0; + sb.AppendLine("@ This file written with the Avid Caption plugin, version 1"); + sb.AppendLine(); + sb.AppendLine(""); + const string writeFormat = "{0} {1}{2}{3}{2}"; + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.AppendLine(string.Format(writeFormat, p.StartTime.ToHHMMSSFF(), EncodeEndTimeCode(p.EndTime), Environment.NewLine, HtmlUtil.RemoveHtmlTags(p.Text, true))); + //00:50:34:22 00:50:39:13 + //Ich muss dafür sorgen, + //dass die Epsteins weiterleben + index++; + } + sb.AppendLine(""); + return sb.ToString(); + } + + private static string EncodeEndTimeCode(TimeCode time) + { + //00:50:39:13 (last is frame) + + //Bugfix for Avid - On 23.976 FPS and 24 FPS projects, when the End time of a subtitle ends in 02, 07, 12, 17, 22, 27 frames, the subtitle won't import. + if (Math.Abs(Configuration.Settings.General.CurrentFrameRate - 23.976) < 0.01 || + Math.Abs(Configuration.Settings.General.CurrentFrameRate - 24) < 0.01) + { + var frames = SubtitleFormat.MillisecondsToFramesMaxFrameRate(time.Milliseconds); + if (frames == 2 || frames == 7 || frames == 12 || frames == 17 || frames == 22 || frames == 27) + frames--; + return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", time.Hours, time.Minutes, time.Seconds, frames); + } + else + { + return time.ToHHMMSSFF(); + } + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + //00:03:15:22 00:03:23:10 This is line one. + //This is line two. + Paragraph p = null; + subtitle.Paragraphs.Clear(); + _errorCount = 0; + bool beginFound = false; + bool endFound = false; + foreach (string line in lines) + { + string tline = line.Trim(); + if (tline.Equals("", StringComparison.OrdinalIgnoreCase)) + { + beginFound = true; + } + else if (tline.Equals("", StringComparison.OrdinalIgnoreCase)) + { + endFound = true; + break; + } + + if (line.IndexOf(':') == 2 && RegexTimeCodes.IsMatch(line)) + { + string temp = line.Substring(0, RegexTimeCodes.Match(line).Length); + string start = temp.Substring(0, 11); + string end = temp.Substring(12, 11); + + string[] startParts = start.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + string[] endParts = end.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + if (startParts.Length == 4 && endParts.Length == 4) + { + p = new Paragraph(DecodeTimeCode(startParts), DecodeTimeCode(endParts), string.Empty); + subtitle.Paragraphs.Add(p); + } + } + else if (tline.Length == 0 || tline[0] == '@') + { + // skip these lines + } + else if (tline.Length > 0 && p != null) + { + if (string.IsNullOrEmpty(p.Text)) + p.Text = line; + else + p.Text = p.Text.TrimEnd() + Environment.NewLine + line; + } + } + if (!beginFound) + _errorCount++; + if (!endFound) + _errorCount++; + + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + //00:00:07:12 + var hour = int.Parse(parts[0]); + var minutes = int.Parse(parts[1]); + var seconds = int.Parse(parts[2]); + var frames = int.Parse(parts[3]); + + return new TimeCode(hour, minutes, seconds, FramesToMillisecondsMax999(frames)); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/AvidDvd.cs b/libse/SubtitleFormats/AvidDvd.cs new file mode 100644 index 000000000..b158e1a0b --- /dev/null +++ b/libse/SubtitleFormats/AvidDvd.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class AvidDvd : SubtitleFormat + { + //25 10:03:20:23 10:03:23:05 some text + //I see, on my way.|New line also. + // + //26 10:03:31:18 10:03:34:00 even more text + //Panessa, why didn't they give them + //an escape route ? + + private static readonly Regex RegexTimeCode = new Regex(@"^\d+\t\d\d:\d\d:\d\d:\d\d\t\d\d:\d\d:\d\d:\d\d\t.+$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Avid DVD"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + if (fileName != null && fileName.EndsWith(".dost", StringComparison.OrdinalIgnoreCase)) + return false; + + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + private static string MakeTimeCode(TimeCode tc) + { + return tc.ToHHMMSSFF(); + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + int count = 1; + bool italic = false; + for (int i = 0; i < subtitle.Paragraphs.Count; i++) + { + Paragraph p = subtitle.Paragraphs[i]; + string text = p.Text; + if (text.StartsWith('{') && text.Length > 6 && text[6] == '}') + text = text.Remove(0, 6); + if (text.StartsWith("", StringComparison.Ordinal) && text.EndsWith("", StringComparison.Ordinal)) + { + if (!italic) + { + italic = true; + sb.AppendLine("$Italic = TRUE"); + } + } + else if (italic) + { + italic = false; + sb.AppendLine("$Italic = FALSE"); + } + + text = HtmlUtil.RemoveHtmlTags(text, true); + sb.AppendLine(string.Format("{0}\t{1}\t{2}\t{3}", count, MakeTimeCode(p.StartTime), MakeTimeCode(p.EndTime), text.Replace(Environment.NewLine, "|"))); + sb.AppendLine(); + count++; + } + + return sb.ToString(); + } + + private static TimeCode DecodeTimeCode(string timeCode) + { + string[] arr = timeCode.Split(new[] { ':', ';', ',' }, StringSplitOptions.RemoveEmptyEntries); + return new TimeCode(int.Parse(arr[0]), int.Parse(arr[1]), int.Parse(arr[2]), FramesToMillisecondsMax999(int.Parse(arr[3]))); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + Paragraph p = null; + var sb = new StringBuilder(); + bool italic = false; + foreach (string line in lines) + { + string s = line.TrimEnd(); + if (RegexTimeCode.IsMatch(s)) + { + try + { + if (p != null) + { + p.Text = sb.ToString().Replace("|", Environment.NewLine).Trim(); + subtitle.Paragraphs.Add(p); + } + sb.Clear(); + string[] arr = s.Split('\t'); + if (arr.Length >= 3) + { + string text = s.Remove(0, arr[0].Length + arr[1].Length + arr[2].Length + 2).Trim(); + + if (string.IsNullOrWhiteSpace(text.Replace("0", string.Empty).Replace("1", string.Empty).Replace("2", string.Empty).Replace("3", string.Empty).Replace("4", string.Empty).Replace("5", string.Empty). + Replace("6", string.Empty).Replace("7", string.Empty).Replace("8", string.Empty).Replace("9", string.Empty).Replace(".", string.Empty).Replace(":", string.Empty).Replace(",", string.Empty))) + _errorCount++; + if (italic) + text = "" + text + ""; + sb.AppendLine(text); + + p = new Paragraph(DecodeTimeCode(arr[1]), DecodeTimeCode(arr[2]), string.Empty); + } + } + catch + { + _errorCount++; + p = null; + } + } + else if (s.StartsWith('$')) + { + if (s.Replace(" ", string.Empty).Equals("$italic=true", StringComparison.OrdinalIgnoreCase)) + { + italic = true; + } + else if (s.Replace(" ", string.Empty).Equals("$italic=false", StringComparison.OrdinalIgnoreCase)) + { + italic = false; + } + } + else if (!string.IsNullOrWhiteSpace(s)) + { + sb.AppendLine(s); + } + } + if (p != null) + { + p.Text = sb.ToString().Replace("|", Environment.NewLine).Trim(); + subtitle.Paragraphs.Add(p); + } + subtitle.Renumber(); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/AvidStl.cs b/libse/SubtitleFormats/AvidStl.cs new file mode 100644 index 000000000..090277971 --- /dev/null +++ b/libse/SubtitleFormats/AvidStl.cs @@ -0,0 +1,192 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class AvidStl : SubtitleFormat + { + private const int TextLength = 112; + + private static Paragraph ReadSubtitleBlock(byte[] buffer, int index) + { + index += 5; + var p = new Paragraph { StartTime = ReadTimeCode(buffer, ref index), EndTime = ReadTimeCode(buffer, ref index) }; + index += 3; + for (int i = index; i < index + TextLength; i++) + { + if (buffer[i] == 0x8f || buffer[i] == 0) + buffer[i] = 32; + else if (buffer[i] == 0x8a) + buffer[i] = 0xa; + } + p.Text = Encoding.GetEncoding(1252).GetString(buffer, index, TextLength).Trim(); + p.Text = p.Text.Replace("\n", Environment.NewLine); + return p; + } + + private static TimeCode ReadTimeCode(byte[] buffer, ref int index) + { + int hours = buffer[index]; + int minutes = buffer[index + 1]; + int seconds = buffer[index + 2]; + int milliseconds = FramesToMillisecondsMax999(buffer[index + 3]); + index += 4; + return new TimeCode(hours, minutes, seconds, milliseconds); + } + + public static void WriteSubtitleBlock(FileStream fs, Paragraph p, int number) + { + fs.WriteByte(0); + fs.WriteByte((byte)(number % 256)); // number - low byte + fs.WriteByte((byte)(number / 256)); // number - high byte + fs.WriteByte(0xff); + fs.WriteByte(0); + WriteTimeCode(fs, p.StartTime); + WriteTimeCode(fs, p.EndTime); + fs.WriteByte(1); + fs.WriteByte(2); + fs.WriteByte(0); + var buffer = Encoding.GetEncoding(1252).GetBytes(p.Text.Replace(Environment.NewLine, "Š")); + if (buffer.Length <= 128) + { + fs.Write(buffer, 0, buffer.Length); + for (int i = buffer.Length; i < TextLength; i++) + { + fs.WriteByte(0x8f); + } + } + else + { + for (int i = 0; i < TextLength; i++) + { + fs.WriteByte(buffer[i]); + } + } + } + + private static void WriteTimeCode(FileStream fs, TimeCode tc) + { + fs.WriteByte((byte)(tc.Hours)); + fs.WriteByte((byte)(tc.Minutes)); + fs.WriteByte((byte)(tc.Seconds)); + fs.WriteByte((byte)(MillisecondsToFramesMaxFrameRate(tc.Milliseconds))); + } + + public override string Extension + { + get { return ".stl"; } + } + + public const string NameOfFormat = "Avid STL"; + + public override string Name + { + get { return NameOfFormat; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public static void Save(string fileName, Subtitle subtitle) + { + using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write)) + { + byte[] buffer = { 0x38, 0x35, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x30, 0x30, 0x30, 0x39 }; + fs.Write(buffer, 0, buffer.Length); + for (int i = 0; i < 0xde; i++) + fs.WriteByte(0); + string numberOfLines = subtitle.Paragraphs.Count.ToString("D5"); + + buffer = Encoding.ASCII.GetBytes(numberOfLines + numberOfLines + "001"); + fs.Write(buffer, 0, buffer.Length); + for (int i = 0; i < 0x15; i++) + fs.WriteByte(0); + buffer = Encoding.ASCII.GetBytes("11"); + fs.Write(buffer, 0, buffer.Length); + while (fs.Length < 1024) + fs.WriteByte(0); + + int subtitleNumber = 0; + foreach (Paragraph p in subtitle.Paragraphs) + { + WriteSubtitleBlock(fs, p, subtitleNumber); + subtitleNumber++; + } + } + } + + public override bool IsMine(List lines, string fileName) + { + if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName)) + { + try + { + var fi = new FileInfo(fileName); + if (fi.Length > 1150 && fi.Length < 1024000) // not too small or too big + { + byte[] buffer = FileUtil.ReadAllBytesShared(fileName); + if (buffer[0] == 0x38 && + buffer[1] == 0x35 && + buffer[2] == 0x30 && + buffer[1024] == 0 && + buffer[1025] == 0 && + buffer[1026] == 0 && + buffer[1027] == 0xff) + { + return true; + } + + if (fileName.EndsWith(".stl", StringComparison.OrdinalIgnoreCase) && + buffer.Length > 1283 && + buffer[1024] == 0 && + buffer[1025] == 1 && + buffer[1026] == 0 && + buffer[1027] == 0xff && + buffer[1152] == 0 && + buffer[1153] == 2 && + buffer[1154] == 0 && + buffer[1155] == 0xff && + buffer[1280] == 0 && + buffer[1281] == 3 && + buffer[1282] == 0 && + buffer[1283] == 0xff) + { + return true; + } + } + } + catch + { + return false; + } + } + return false; + } + + public override string ToText(Subtitle subtitle, string title) + { + return "Not supported!"; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + subtitle.Paragraphs.Clear(); + subtitle.Header = null; + byte[] buffer = FileUtil.ReadAllBytesShared(fileName); + + int index = 1024; + while (index <= buffer.Length - 128) + { + Paragraph p = ReadSubtitleBlock(buffer, index); + subtitle.Paragraphs.Add(p); + index += 128; + } + subtitle.Renumber(); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/Ayato.cs b/libse/SubtitleFormats/Ayato.cs new file mode 100644 index 000000000..62823f6e8 --- /dev/null +++ b/libse/SubtitleFormats/Ayato.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class Ayato : SubtitleFormat + { + public override string Extension + { + get { return "aya"; } + } + + public override string Name + { + get { return "Ayato"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName)) + { + var fi = new FileInfo(fileName); + if (fi.Length >= 3000 && fi.Length < 1024000) // not too small or too big + { + if (!fileName.EndsWith(".aya", StringComparison.OrdinalIgnoreCase)) + return false; + + var sub = new Subtitle(); + LoadSubtitle(sub, lines, fileName); + return sub.Paragraphs.Count > 0; + } + } + return false; + } + + public override string ToText(Subtitle subtitle, string title) + { + throw new NotImplementedException(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + const int startPosition = 0xa99; + const int textPosition = 72; + + _errorCount = 0; + subtitle.Paragraphs.Clear(); + subtitle.Header = null; + var buffer = FileUtil.ReadAllBytesShared(fileName); + int index = startPosition; + if (buffer[index] != 1) + { + return; + } + + while (index + textPosition < buffer.Length) + { + int textLength = buffer[index + 16]; + if (textLength > 0 && index + textPosition + textLength < buffer.Length) + { + string text = GetText(index + textPosition, textLength, buffer); + if (!string.IsNullOrWhiteSpace(text)) + { + int startFrames = GetFrames(index + 4, buffer); + int endFrames = GetFrames(index + 8, buffer); + subtitle.Paragraphs.Add(new Paragraph(text, FramesToMilliseconds(startFrames), FramesToMilliseconds(endFrames))); + } + } + index += textPosition + textLength; + } + subtitle.Renumber(); + } + + private static string GetText(int index, int length, byte[] buffer) + { + if (length < 1) + { + return string.Empty; + } + + int offset = 0; + if (buffer[index] == 7) + { + offset = 1; + } + else if (buffer[index + 1] == 7) + { + offset = 2; + } + else if (buffer[index + 2] == 7) + { + offset = 3; + } + + if (length - offset < 1) + { + return string.Empty; + } + + const string newline1 = " "; // unicode chars + const string newline2 = ""; // unicode char + var s = Encoding.UTF8.GetString(buffer, index + offset, length - offset); + s = s.Replace(newline1, Environment.NewLine); + s = s.Replace(newline2, Environment.NewLine); + return s; + } + + private static int GetFrames(int index, byte[] buffer) + { + return (buffer[index + 2] << 16) + (buffer[index + 1] << 8) + buffer[index]; + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/BdnXml.cs b/libse/SubtitleFormats/BdnXml.cs new file mode 100644 index 000000000..98049a912 --- /dev/null +++ b/libse/SubtitleFormats/BdnXml.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class BdnXml : SubtitleFormat + { + public override string Extension + { + get { return ".xml"; } + } + + public override string Name + { + get { return "BDN Xml"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > 0; + } + + public override string ToText(Subtitle subtitle, string title) + { + string xmlStructure = + "" + Environment.NewLine + + ""; + + var xml = new XmlDocument { XmlResolver = null }; + xml.LoadXml(xmlStructure); + + foreach (Paragraph p in subtitle.Paragraphs) + { + XmlNode paragraph = xml.CreateElement("Paragraph"); + + XmlNode number = xml.CreateElement("Number"); + number.InnerText = p.Number.ToString(CultureInfo.InvariantCulture); + paragraph.AppendChild(number); + + XmlNode start = xml.CreateElement("StartMilliseconds"); + start.InnerText = p.StartTime.TotalMilliseconds.ToString(CultureInfo.InvariantCulture); + paragraph.AppendChild(start); + + XmlNode end = xml.CreateElement("EndMilliseconds"); + end.InnerText = p.EndTime.TotalMilliseconds.ToString(CultureInfo.InvariantCulture); + paragraph.AppendChild(end); + + XmlNode text = xml.CreateElement("Text"); + text.InnerText = HtmlUtil.RemoveHtmlTags(p.Text, true); + paragraph.AppendChild(text); + + xml.DocumentElement.AppendChild(paragraph); + } + + var ms = new MemoryStream(); + var writer = new XmlTextWriter(ms, Encoding.UTF8) { Formatting = Formatting.Indented }; + xml.Save(writer); + return Encoding.UTF8.GetString(ms.ToArray()).Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + + string xmlString = sb.ToString(); + if (!xmlString.Contains(" lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string paragraphWriteFormat = "/tc {0} {1}{2}{3}{2}"; + + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.AppendLine(string.Format(paragraphWriteFormat, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), Environment.NewLine, EncodeText(p.Text))); + } + + var doc = new XmlDocument { XmlResolver = null }; + doc.LoadXml("" + Environment.NewLine + @" + document + + creator + SICT + type + STLI + version + 1.4 + applicationversion + Belle Nuit Subtitler 1.7.8 + creationdate + 2012-03-13 16:30:32 + modificationdate + 2012-03-13 16:30:32 + + mainleft + 40 + maintop + 48 + mainwidth + 825 + mainheight + 886 + styledt + + exportdt + + previewdt + + moviedt + + exportformat + TIFF + style + + font + Geneva + size + 26 + spacing + 1 + leading + 7 + bold + + italic + + underline + + vertical + 486 + halin + 1 + valign + 2 + standard + PAL + height + 576 + width + 720 + widthreal + 768 + antialiasing + 4 + left + 40 + right + 680 + wrapmethod + 2 + interlaced + + textcolor + #FBFFF2 + textalpha + 1 + textsoft + 0 + bordercolor + #F0F10 + borderalpha + 1 + bordersoft + 0 + borderwidth + 6 + rectcolor + #0 + rectalpha + 0 + rectsoft + 0 + rectform + 1 + shadowcolor + #7F7F7F + shadowalpha + 0 + shadowsoft + 0 + shadowx + 2 + shadowy + 2 + framerate + 25 + + folderpath + + prefix + + moviepath + + movieoffset + 00:00:00:00 + moviesyncoption + + pagesetup + + titlelist +"); + XmlNode node = doc.CreateElement("string"); + node.InnerText = sb.ToString().Trim() + Environment.NewLine + Environment.NewLine; + doc.DocumentElement.AppendChild(node); + + return ToUtf8XmlString(doc).Replace("\r\n", "\n"); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + var sb = new StringBuilder(); + foreach (var line in lines) + { + sb.AppendLine(line); + } + var doc = new XmlDocument { XmlResolver = null }; + try + { + doc.LoadXml(sb.ToString()); + if (doc.DocumentElement == null || doc.DocumentElement.Name != "xmldict" || doc.DocumentElement.SelectSingleNode("string") == null) + return; + } + catch (Exception) + { + _errorCount = 1; + return; + } + + string text = null; + string keyName = string.Empty; + foreach (XmlNode node in doc.DocumentElement.ChildNodes) + { + if (node.Name == "key") + { + keyName = node.InnerText; + } + else if (node.Name == "string" && keyName == "titlelist") + { + text = node.InnerText; + break; + } + } + if (text == null) + return; + + subtitle.Paragraphs.Clear(); + Paragraph paragraph = null; + sb = new StringBuilder(); + foreach (string line in text.Split(Utilities.NewLineChars)) + { + if (RegexTimeCode.IsMatch(line)) + { + string[] parts = line.Substring(4, 11).Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 4) + { + try + { + if (paragraph != null && !string.IsNullOrWhiteSpace(sb.ToString())) + { + paragraph.Text = DecodeText(sb); + } + + var start = DecodeTimeCode(parts); + parts = line.Substring(16, 11).Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + var end = DecodeTimeCode(parts); + paragraph = new Paragraph { StartTime = start, EndTime = end }; + subtitle.Paragraphs.Add(paragraph); + sb.Clear(); + } + catch + { + _errorCount++; + } + } + } + else if (RegexFileNum.IsMatch(line)) + { + continue; // skip Belle-Nuit's numbering lines ("/file 0001") + } + else if (paragraph != null) + { + sb.AppendLine(line); + } + else + { + _errorCount++; + } + } + if (paragraph != null && !string.IsNullOrWhiteSpace(sb.ToString())) + { + paragraph.Text = DecodeText(sb); + } + subtitle.Renumber(); + } + + private static string EncodeText(string s) + { + s = HtmlUtil.RemoveOpenCloseTags(s, HtmlUtil.TagBold, HtmlUtil.TagUnderline, HtmlUtil.TagFont); + if (s.StartsWith("{\\an3}", StringComparison.Ordinal) || s.StartsWith("{\\an6}", StringComparison.Ordinal)) + s = "/STYLE RIGHT" + Environment.NewLine + s.Remove(0, 6).Trim(); + if (s.StartsWith("{\\an1}", StringComparison.Ordinal) || s.StartsWith("{\\an4}", StringComparison.Ordinal)) + s = "/STYLE LEFT" + Environment.NewLine + s.Remove(0, 6).Trim(); + if (s.StartsWith("{\\an7}", StringComparison.Ordinal) || s.StartsWith("{\\an8}", StringComparison.Ordinal) || s.StartsWith("{\\an9}", StringComparison.Ordinal)) + s = "/STYLE VERTICAL(-25)" + Environment.NewLine + s.Remove(0, 6).Trim(); + if (s.StartsWith("{\\an2}", StringComparison.Ordinal) || s.StartsWith("{\\an5}", StringComparison.Ordinal)) + s = s.Remove(0, 6).Trim(); + return s; + } + + private static string DecodeText(StringBuilder sb) + { + var s = sb.ToString().Trim(); + s = s.Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine).Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine); + if (s.StartsWith("/STYLE RIGHT" + Environment.NewLine, StringComparison.Ordinal)) + s = "{\\an3}" + s.Remove(0, 12).Trim(); + if (s.StartsWith("/STYLE LEFT" + Environment.NewLine, StringComparison.Ordinal)) + s = "{\\an1}" + s.Remove(0, 11).Trim(); + if (s.StartsWith("/STYLE TOP" + Environment.NewLine, StringComparison.Ordinal)) + s = "{\\an8}" + s.Remove(0, 10).Trim(); + if (s.StartsWith("/STYLE VERTICAL(-25)" + Environment.NewLine, StringComparison.Ordinal)) + s = "{\\an8}" + s.Remove(0, 20).Trim(); + if (s.StartsWith("/STYLE VERTICAL(-24)" + Environment.NewLine, StringComparison.Ordinal)) + s = "{\\an8}" + s.Remove(0, 20).Trim(); + if (s.StartsWith("/STYLE VERTICAL(-23)" + Environment.NewLine, StringComparison.Ordinal)) + s = "{\\an8}" + s.Remove(0, 20).Trim(); + if (s.StartsWith("/STYLE VERTICAL(-22)" + Environment.NewLine, StringComparison.Ordinal)) + s = "{\\an8}" + s.Remove(0, 20).Trim(); + if (s.StartsWith("/STYLE VERTICAL(-21)" + Environment.NewLine, StringComparison.Ordinal)) + s = "{\\an8}" + s.Remove(0, 20).Trim(); + if (s.StartsWith("/STYLE VERTICAL(-20)" + Environment.NewLine, StringComparison.Ordinal)) + s = "{\\an8}" + s.Remove(0, 20).Trim(); + if (s.StartsWith("/STYLE VERTICAL(-19)" + Environment.NewLine, StringComparison.Ordinal)) + s = "{\\an8}" + s.Remove(0, 20).Trim(); + if (s.StartsWith("/STYLE VERTICAL(-18)" + Environment.NewLine, StringComparison.Ordinal)) + s = "{\\an5}" + s.Remove(0, 20).Trim(); + if (s.StartsWith("/STYLE VERTICAL(-17)" + Environment.NewLine, StringComparison.Ordinal)) + s = "{\\an5}" + s.Remove(0, 20).Trim(); + if (s.StartsWith("/STYLE VERTICAL(-16)" + Environment.NewLine, StringComparison.Ordinal)) + s = "{\\an5}" + s.Remove(0, 20).Trim(); + if (s.StartsWith("/STYLE VERTICAL(-15)" + Environment.NewLine, StringComparison.Ordinal)) + s = "{\\an5}" + s.Remove(0, 20).Trim(); + if (s.StartsWith("/STYLE VERTICAL(-14)" + Environment.NewLine, StringComparison.Ordinal)) + s = "{\\an5}" + s.Remove(0, 20).Trim(); + if (s.StartsWith("/STYLE VERTICAL(-13)" + Environment.NewLine, StringComparison.Ordinal)) + s = "{\\an5}" + s.Remove(0, 20).Trim(); + if (s.StartsWith("/STYLE VERTICAL(-12)" + Environment.NewLine, StringComparison.Ordinal)) + s = "{\\an5}" + s.Remove(0, 20).Trim(); + if (s.StartsWith("/STYLE VERTICAL(-11)" + Environment.NewLine, StringComparison.Ordinal)) + s = "{\\an5}" + s.Remove(0, 20).Trim(); + if (s.StartsWith("/STYLE VERTICAL(-10)" + Environment.NewLine, StringComparison.Ordinal)) + s = "{\\an5}" + s.Remove(0, 20).Trim(); + s = HtmlUtil.FixInvalidItalicTags(s); + return s; + } + + private static string EncodeTimeCode(TimeCode time) + { + return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", time.Hours, time.Minutes, time.Seconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + var hour = int.Parse(parts[0]); + var minutes = int.Parse(parts[1]); + var seconds = int.Parse(parts[2]); + var frames = int.Parse(parts[3]); + + return new TimeCode(hour, minutes, seconds, FramesToMillisecondsMax999(frames)); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/CapMakerPlus.cs b/libse/SubtitleFormats/CapMakerPlus.cs new file mode 100644 index 000000000..25e2f973a --- /dev/null +++ b/libse/SubtitleFormats/CapMakerPlus.cs @@ -0,0 +1,293 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class CapMakerPlus : SubtitleFormat + { + private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d:\d\d:\d\d:\d\d$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".cap"; } + } + + public const string NameOfFormat = "CapMaker Plus"; + + public override string Name + { + get { return NameOfFormat; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public static void Save(string fileName, Subtitle subtitle) + { + Paragraph p; + int gridDataCount = subtitle.Paragraphs.Count; + for (int i = 0; i < subtitle.Paragraphs.Count; i++) + { + p = subtitle.Paragraphs[i]; + Paragraph next = subtitle.GetParagraphOrDefault(i + 1); + if (next != null && next.StartTime.TotalMilliseconds - p.EndTime.TotalMilliseconds > 100) + gridDataCount++; + } + + var buffer = new byte[] { 0x2B, 0x27, 0xF, 0x3C, 0x43, 0x61, 0x70, 0x4D, 0x61, 0x6B, 0x65, 0x72, 0x20, 0x50, 0x6C, 0x75, 0x73, 0x3E, 0x3, 0x2, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x21, 0, 0, 0, 0, 0, 0, 0, 0x7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x5, 0, 0, 0, 0x5, 0, 0, 0, 0x5, 0, 0, 0, 0x5, 0, 0, 0, 0x1, 0, 0, 0, 0x5E, 0x1, 0, 0, 0x1E, 0, 0, 0, 0x20, 0, 0, 0, 0x2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x7D, 0, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0x80, 0x80, 0x80, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0x80, 0x80, 0x80, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x42, 0, 0, 0, 0x30, 0, 0, 0, 0x42, 0, 0, 0, 0x30, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0x80, 0x80, 0x80, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0x20, 0, 0x80, 0x80, 0x80, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0x2, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10, 0x10, 0x10, 0, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0x80, 0x80, 0x80, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0x80, 0x80, 0x80, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0x80, 0x80, 0x80, 0, 0x1, 0, 0, 0, 0xFF, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x40, 0x40, 0x40, 0, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0x80, 0x80, 0x80, 0, 0x1, 0, 0, 0, 0xFF, 0, 0, 0, 0x1, 0, 0, 0, 0x2, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0x1, 0, 0x9, 0, 0x43, 0x47, 0x72, 0x69, 0x64, 0x44, 0x61, 0x74, 0x61, 0x3, 0x2, 0x25, 0, 0, 0, 0, 0, 0, 0, 0x9, 0x4C, 0x61, 0x6E, 0x67, 0x75, 0x61, 0x67, 0x65, 0x31, 0x1, 0, 0xFF, 0, 0x1, 0, 0, 0, 0, 0, 0, 0x80, 0xBF, 0, 0, 0, 0xC0, 0x2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x9, 0x56, 0x69, 0x64, 0x65, 0x6F, 0x2E, 0x63, 0x61, 0x70, 0x9, 0x56, 0x69, 0x64, 0x65, 0x6F, 0x2E, 0x63, 0x61, 0x70, 0x1F, 0x44, 0x3A, 0x5C, 0x43, 0x70, 0x63, 0x57, 0x69, 0x6E, 0x5C, 0x37, 0x30, 0x30, 0x5C, 0x53, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x73, 0x5C, 0x56, 0x69, 0x64, 0x65, 0x6F, 0x2E, 0x74, 0x78, 0x74, 0x3C, 0, 0, 0, 0x5, 0x41, 0x72, 0x69, 0x61, 0x6C, 0, 0, 0, 0x40, 0xE0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x3C, 0, 0, 0, 0xB, 0x43, 0x6F, 0x75, 0x72, 0x69, 0x65, 0x72, 0x20, 0x4E, 0x65, 0x77, 0, 0, 0, 0x40, 0xB4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0xF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFB, 0xFF, 0x4, 0, 0xC, 0, 0, 0, 0x1E, 0, 0x1E, 0, 0x1E, 0, 0x1E, 0, 0x1E, 0, 0x1E, 0, 0x1E, 0, 0x1E, 0, 0x1E, 0, 0x1E, 0, 0x1E, 0, 0x1E, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x3, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x40, 0x1, 0, 0, 0xF0, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x14, 0, 0, 0, 0xA, 0, 0, 0, 0x14, 0, 0, 0, 0xA, 0, 0, 0, 0, 0, 0, 0x9, 0x56, 0x69, 0x64, 0x65, 0x6F, 0x2E, 0x63, 0x61, 0x70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x3C, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0 }; + buffer[1400] = (byte)(gridDataCount % 256); // paragraphs - low byte + buffer[1401] = (byte)(gridDataCount / 256); // paragraphs - high byte + + using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write)) + { + fs.Write(buffer, 0, buffer.Length); + + p = null; + for (int i = 0; i < subtitle.Paragraphs.Count; i++) + { + p = subtitle.Paragraphs[i]; + Paragraph next = subtitle.GetParagraphOrDefault(i + 1); + + WriteTime(fs, p.StartTime); + + buffer = new byte[] { + // styles 00 00 80 BF 00 00 00 C0 02 00 01 00 + 0, + 0, + 0x80, //horizontal align, 0x80BF= center, 0x0000=left, 0x00c0=right + 0xBF, + 0, + 0, + 0, + 0xC0, // vertical Position: C0=bottom, 0=top + 2, //justification, 1=left, 2=center + 0, + 1, //1=normal font, 3=italic + 0 + }; + + string text = p.Text; + if (text.StartsWith("{\\a6}", StringComparison.Ordinal)) + { + text = p.Text.Remove(0, 5); + buffer[7] = 0; // align top + } + else if (text.StartsWith("{\\a1}", StringComparison.Ordinal)) + { + text = p.Text.Remove(0, 5); + buffer[2] = 0; // align left + buffer[3] = 0; // align left + } + else if (text.StartsWith("{\\a3}", StringComparison.Ordinal)) + { + text = p.Text.Remove(0, 5); + buffer[2] = 0; // align right + buffer[3] = 0xc0; // align right + } + else if (text.StartsWith("{\\a5}", StringComparison.Ordinal)) + { + text = p.Text.Remove(0, 5); + buffer[7] = 0; // align top + buffer[2] = 0; // align left + buffer[3] = 0; // align left + } + else if (text.StartsWith("{\\a7}", StringComparison.Ordinal)) + { + text = p.Text.Remove(0, 5); + buffer[7] = 0; // align top + buffer[2] = 0; // align right + buffer[3] = 0xc0; // align right + } + + if (text.StartsWith("", StringComparison.Ordinal) && text.EndsWith("", StringComparison.Ordinal)) + buffer[10] = 3; + fs.Write(buffer, 0, buffer.Length); + + text = HtmlUtil.RemoveHtmlTags(text); + if (text.Length > 118) + text = text.Substring(0, 118); + fs.WriteByte((byte)(text.Length)); + buffer = Encoding.GetEncoding(1252).GetBytes(text); + fs.Write(buffer, 0, buffer.Length); + + for (int j = 0; j < 74; j++) + fs.WriteByte(0); + + if (next != null && next.StartTime.TotalMilliseconds - p.EndTime.TotalMilliseconds > 100) + { + // write empty end + WriteTime(fs, p.EndTime); + buffer = new byte[] { 0, 0, 0, 0xC0, 0, 0, 0, 0, 0x01, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + fs.Write(buffer, 0, buffer.Length); + } + } + if (p != null) + { + WriteTime(fs, p.EndTime); + buffer = new byte[] { 0, 0, 0x80, 0xBF, 0, 0, 0, 0xC0, 0x02, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x40, 0x40, 0x40, 0, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0x80, 0x80, 0x80, 0, 0x01, 0, 0, 0, 0xFF, 0, 0, 0, 0x01, 0, 0, 0, 0x02, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + fs.Write(buffer, 0, buffer.Length); + } + } + } + + private static void WriteTime(FileStream fs, TimeCode timeCode) + { + fs.WriteByte(0xb); + byte[] buffer = Encoding.ASCII.GetBytes(timeCode.ToHHMMSSFF()); + fs.Write(buffer, 0, buffer.Length); + } + + public override bool IsMine(List lines, string fileName) + { + if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName)) + { + var fi = new FileInfo(fileName); + if (fi.Length >= 640 && fi.Length < 1024000) // not too small or too big + { + if (fileName.EndsWith(".cap", StringComparison.OrdinalIgnoreCase)) + { + byte[] buffer = FileUtil.ReadAllBytesShared(fileName); + if (buffer[0] == 0x2b) // "+" + return true; + } + } + } + return false; + } + + public override string ToText(Subtitle subtitle, string title) + { + return "Not supported!"; + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + //00:00:07:12 + var hour = int.Parse(parts[0]); + var minutes = int.Parse(parts[1]); + var seconds = int.Parse(parts[2]); + var frames = int.Parse(parts[3]); + + return new TimeCode(hour, minutes, seconds, FramesToMillisecondsMax999(frames)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + subtitle.Paragraphs.Clear(); + subtitle.Header = null; + byte[] buffer = FileUtil.ReadAllBytesShared(fileName); + + int i = 128; + Paragraph last = null; + while (i < buffer.Length - 20) + { + if (buffer[i] == 0x0b) + { + string timeCode = Encoding.ASCII.GetString(buffer, i + 1, 11); + if (timeCode != "00:00:00:00" && RegexTimeCodes.IsMatch(timeCode)) + { + var p = new Paragraph { StartTime = DecodeTimeCode(timeCode.Split(':')) }; + bool italic = buffer[i + 22] == 3; // 3=italic, 1=normal + int textStart = i + 25; // text starts 25 chars after time code + int textLength = 0; + while (textStart + textLength < buffer.Length && buffer[textStart + textLength] != 0) + { + textLength++; + } + if (textLength > 0) + { + p.Text = Encoding.GetEncoding(1252).GetString(buffer, textStart, textLength); + int rtIndex = p.Text.IndexOf("{\\rtf1", StringComparison.Ordinal); + if (rtIndex >= 0 && rtIndex < 10) + { + var rtBox = new System.Windows.Forms.RichTextBox(); + try + { + rtBox.Rtf = p.Text.Substring(rtIndex); + p.Text = rtBox.Text; + } + catch (Exception exception) + { + System.Diagnostics.Debug.WriteLine(exception.Message); + } + rtBox.Dispose(); + } + else if (italic) + { + p.Text = "" + p.Text + ""; + } + } + else + { + p.Text = string.Empty; + } + last = p; + subtitle.Paragraphs.Add(p); + } + } + i++; + } + if (last != null) + last.EndTime.TotalMilliseconds = last.StartTime.TotalMilliseconds + Utilities.GetOptimalDisplayMilliseconds(last.Text); + + for (i = 0; i < subtitle.Paragraphs.Count - 1; i++) + { + subtitle.Paragraphs[i].EndTime.TotalMilliseconds = subtitle.Paragraphs[i + 1].StartTime.TotalMilliseconds; + } + for (i = subtitle.Paragraphs.Count - 1; i >= 0; i--) + { + if (string.IsNullOrEmpty(subtitle.Paragraphs[i].Text)) + subtitle.Paragraphs.RemoveAt(i); + } + + var deletes = new List(); + for (i = 0; i < subtitle.Paragraphs.Count - 1; i++) + { + if (subtitle.Paragraphs[i].StartTime.TotalMilliseconds == subtitle.Paragraphs[i + 1].StartTime.TotalMilliseconds) + { + subtitle.Paragraphs[i].Text += Environment.NewLine + subtitle.Paragraphs[i + 1].Text; + subtitle.Paragraphs[i].EndTime = subtitle.Paragraphs[i + 1].EndTime; + deletes.Add(i + 1); + } + } + deletes.Reverse(); + foreach (int index in deletes) + { + subtitle.Paragraphs.RemoveAt(index); + } + + for (i = 0; i < subtitle.Paragraphs.Count - 1; i++) + { + if (subtitle.Paragraphs[i].StartTime.TotalMilliseconds == subtitle.Paragraphs[i + 1].StartTime.TotalMilliseconds) + { + } + else if (subtitle.Paragraphs[i].EndTime.TotalMilliseconds == subtitle.Paragraphs[i + 1].StartTime.TotalMilliseconds) + { + subtitle.Paragraphs[i].EndTime.TotalMilliseconds = subtitle.Paragraphs[i + 1].StartTime.TotalMilliseconds - 1; + } + } + subtitle.Renumber(); + + // adjust all times + if (buffer.Length > 1364) + { + try + { + string adjust = Encoding.GetEncoding(1252).GetString(buffer, 1354, 11); // 00:59:59:28 + TimeCode tc = DecodeTimeCode(adjust.Split(':')); + if (tc.TotalMilliseconds > 0) + subtitle.AddTimeToAllParagraphs(TimeSpan.FromMilliseconds(-tc.TotalMilliseconds)); + } + catch + { + } + } + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/CaptionAssistant.cs b/libse/SubtitleFormats/CaptionAssistant.cs new file mode 100644 index 000000000..f3245af99 --- /dev/null +++ b/libse/SubtitleFormats/CaptionAssistant.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class CaptionAssistant : SubtitleFormat + { + public override string Extension + { + get { return ".cac"; } + } + + public override string Name + { + get { return "Caption Assistant"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > 0; + } + + private static string ToTimeCode(TimeCode time) + { + return time.ToHHMMSSFF(); + } + + private static TimeCode DecodeTimeCode(string s) + { + var parts = s.Split(new[] { ':', ';' }, StringSplitOptions.RemoveEmptyEntries); + var hour = int.Parse(parts[0]); + var minutes = int.Parse(parts[1]); + var seconds = int.Parse(parts[2]); + var frames = int.Parse(parts[3]); + + int milliseconds = (int)Math.Round(((TimeCode.BaseUnit / Configuration.Settings.General.CurrentFrameRate) * frames)); + if (milliseconds > 999) + milliseconds = 999; + + return new TimeCode(hour, minutes, seconds, milliseconds); + } + + public override string ToText(Subtitle subtitle, string title) + { + string xmlStructure = + "" + Environment.NewLine + + ""; + + var xml = new XmlDocument(); + xml.LoadXml(xmlStructure); + xml.XmlResolver = null; + var cd = xml.DocumentElement.SelectSingleNode("CaptionData"); + foreach (Paragraph p in subtitle.Paragraphs) + { + XmlNode paragraph = xml.CreateElement("CaptionDetail"); + + XmlAttribute start = xml.CreateAttribute("PositionIn"); + start.InnerText = ToTimeCode(p.StartTime); + paragraph.Attributes.Append(start); + + XmlAttribute end = xml.CreateAttribute("PositionOut"); + end.InnerText = ToTimeCode(p.EndTime); + paragraph.Attributes.Append(end); + + XmlAttribute text = xml.CreateAttribute("CaptionText"); + text.InnerText = HtmlUtil.RemoveHtmlTags(p.Text, true); + paragraph.Attributes.Append(text); + + XmlAttribute align = xml.CreateAttribute("Align"); + if (p.Text.StartsWith("{\\an1}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an4}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an7}", StringComparison.Ordinal)) + align.InnerText = "Left"; + else if (p.Text.StartsWith("{\\an3}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an6}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an9}", StringComparison.Ordinal)) + align.InnerText = "Right"; + else + align.InnerText = "Center"; + + paragraph.Attributes.Append(align); + + XmlAttribute captionType = xml.CreateAttribute("CaptionType"); + captionType.InnerText = "608CC1"; + paragraph.Attributes.Append(captionType); + + cd.AppendChild(paragraph); + } + + return ToUtf8XmlString(xml); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + + string allText = sb.ToString(); + if (!allText.Contains("") || !allText.Contains("")) + return; + + var xml = new XmlDocument { XmlResolver = null }; + try + { + xml.LoadXml(allText); + } + catch (Exception exception) + { + System.Diagnostics.Debug.WriteLine(exception.Message); + _errorCount = 1; + return; + } + + if (xml.DocumentElement == null) + { + _errorCount = 1; + return; + } + + foreach (XmlNode node in xml.DocumentElement.SelectNodes("CaptionData/CaptionDetail")) + { + try + { + if (node.Attributes != null) + { + string text = node.Attributes.GetNamedItem("CaptionText").InnerText.Trim(); + + if (node.Attributes.GetNamedItem("Align") != null) + { + string align = node.Attributes.GetNamedItem("Align").InnerText.Trim(); + if (align.Equals("left", StringComparison.OrdinalIgnoreCase)) + text = "{\\an1}" + text; + else if (align.Equals("right", StringComparison.OrdinalIgnoreCase)) + text = "{\\an3}" + text; + } + + string start = node.Attributes.GetNamedItem("PositionIn").InnerText; + string end = node.Attributes.GetNamedItem("PositionOut").InnerText; + subtitle.Paragraphs.Add(new Paragraph(DecodeTimeCode(start), DecodeTimeCode(end), text)); + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + _errorCount++; + } + } + subtitle.Renumber(); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/Captionate.cs b/libse/SubtitleFormats/Captionate.cs new file mode 100644 index 000000000..08719f635 --- /dev/null +++ b/libse/SubtitleFormats/Captionate.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class Captionate : SubtitleFormat + { + public override string Extension + { + get { return ".xml"; } + } + + public const string NameOfFormat = "Captionate"; + + public override string Name + { + get { return NameOfFormat; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > 0; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string xmlStructure = @" +hh:mm:ss:ff/30 +namesareprefixed + + + +Default + + +140 + + + + + + +"; + + var xml = new XmlDocument(); + xml.LoadXml(xmlStructure); + + Paragraph last = null; + foreach (Paragraph p in subtitle.Paragraphs) + { + if (last != null) + { + if (last.EndTime.TotalMilliseconds + 500 < p.StartTime.TotalMilliseconds) + { + var blank = new Paragraph { StartTime = { TotalMilliseconds = last.EndTime.TotalMilliseconds } }; + AddParagraph(xml, blank); + } + } + + AddParagraph(xml, p); + last = p; + } + + return ToUtf8XmlString(xml, true); + } + + private static void AddParagraph(XmlDocument xml, Paragraph p) + { + XmlNode paragraph = xml.CreateElement("caption"); + + XmlAttribute start = xml.CreateAttribute("time"); + start.InnerText = EncodeTime(p.StartTime); + paragraph.Attributes.Append(start); + + if (!string.IsNullOrWhiteSpace(p.Text)) + { + XmlNode tracks = xml.CreateElement("tracks"); + paragraph.AppendChild(tracks); + + XmlNode track0 = xml.CreateElement("track0"); + track0.InnerText = HtmlUtil.RemoveHtmlTags(p.Text, true); + track0.InnerXml = track0.InnerXml.Replace(Environment.NewLine, "
"); + tracks.AppendChild(track0); + } + xml.DocumentElement.SelectSingleNode("captions").AppendChild(paragraph); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + string xmlString; + + if (lines == null && fileName != null) + { + xmlString = File.ReadAllText(fileName); + } + else if (lines != null) + { + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + xmlString = sb.ToString(); + } + else + { + return; + } + + if (!xmlString.Contains("") || !xmlString.Contains("")) + return; + + var xml = new XmlDocument { XmlResolver = null }; + try + { + xml.LoadXml(xmlString); + } + catch + { + _errorCount = 1; + return; + } + + Paragraph p = null; + foreach (XmlNode node in xml.DocumentElement.SelectNodes("captions/caption")) + { + try + { + if (node.Attributes["time"] != null) + { + string start = node.Attributes["time"].InnerText; + double startMilliseconds = DecodeTimeToMilliseconds(start); + if (p != null) + p.EndTime.TotalMilliseconds = startMilliseconds - 1; + if (node.SelectSingleNode("tracks/track0") != null) + { + string text = node.SelectSingleNode("tracks/track0").InnerText; + text = HtmlUtil.RemoveHtmlTags(text); + text = text.Replace("
", Environment.NewLine).Replace("
", Environment.NewLine).Replace("
", Environment.NewLine); + p = new Paragraph(text, startMilliseconds, startMilliseconds + 3000); + if (!string.IsNullOrWhiteSpace(text)) + subtitle.Paragraphs.Add(p); + } + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + _errorCount++; + } + } + subtitle.Renumber(); + } + + private static double DecodeTimeToMilliseconds(string time) + { + string[] parts = time.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + return new TimeSpan(0, int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), (int)(int.Parse(parts[3]) * 10.0)).TotalMilliseconds; + } + + private static string EncodeTime(TimeCode time) + { + return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", time.Hours, time.Minutes, time.Seconds, time.Milliseconds / 10.0); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/CaptionateMs.cs b/libse/SubtitleFormats/CaptionateMs.cs new file mode 100644 index 000000000..b37f66dbd --- /dev/null +++ b/libse/SubtitleFormats/CaptionateMs.cs @@ -0,0 +1,155 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class CaptionateMs : SubtitleFormat + { + public override string Extension + { + get { return ".xml"; } + } + + public override string Name + { + get { return "Captionate MS"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > 0; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string xmlStructure = @" +ms +namesareprefixed + + + +Default + + +140 + + + + + + +"; + + var xml = new XmlDocument(); + xml.LoadXml(xmlStructure); + + Paragraph last = null; + foreach (Paragraph p in subtitle.Paragraphs) + { + if (last != null) + { + if (last.EndTime.TotalMilliseconds + 500 < p.StartTime.TotalMilliseconds) + { + var blank = new Paragraph { StartTime = { TotalMilliseconds = last.EndTime.TotalMilliseconds } }; + AddParagraph(xml, blank); + } + } + + AddParagraph(xml, p); + last = p; + } + + return ToUtf8XmlString(xml, true); + } + + private static void AddParagraph(XmlDocument xml, Paragraph p) + { + XmlNode paragraph = xml.CreateElement("caption"); + + XmlAttribute start = xml.CreateAttribute("time"); + start.InnerText = EncodeTime(p.StartTime); + paragraph.Attributes.Append(start); + + if (!string.IsNullOrWhiteSpace(p.Text)) + { + XmlNode tracks = xml.CreateElement("tracks"); + paragraph.AppendChild(tracks); + + XmlNode track0 = xml.CreateElement("track0"); + track0.InnerText = HtmlUtil.RemoveHtmlTags(p.Text, true); + track0.InnerXml = track0.InnerXml.Replace(Environment.NewLine, "
"); + tracks.AppendChild(track0); + } + xml.DocumentElement.SelectSingleNode("captions").AppendChild(paragraph); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + + string xmlString = sb.ToString(); + if (!xmlString.Contains("") || !xmlString.Contains("")) + return; + + var xml = new XmlDocument { XmlResolver = null }; + try + { + xml.LoadXml(xmlString); + } + catch + { + _errorCount = 1; + return; + } + + Paragraph p = null; + foreach (XmlNode node in xml.DocumentElement.SelectNodes("captions/caption")) + { + try + { + if (node.Attributes["time"] != null) + { + string start = node.Attributes["time"].InnerText; + double startMilliseconds = double.Parse(start); + if (p != null) + p.EndTime.TotalMilliseconds = startMilliseconds - 1; + if (node.SelectSingleNode("tracks/track0") != null) + { + string text = node.SelectSingleNode("tracks/track0").InnerText; + text = HtmlUtil.RemoveHtmlTags(text); + text = text.Replace("
", Environment.NewLine).Replace("
", Environment.NewLine).Replace("
", Environment.NewLine); + p = new Paragraph(text, startMilliseconds, startMilliseconds + 3000); + if (!string.IsNullOrWhiteSpace(text)) + subtitle.Paragraphs.Add(p); + } + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + _errorCount++; + } + } + subtitle.Renumber(); + } + + private static string EncodeTime(TimeCode time) + { + return time.TotalMilliseconds.ToString(CultureInfo.InvariantCulture); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/CaptionsInc.cs b/libse/SubtitleFormats/CaptionsInc.cs new file mode 100644 index 000000000..e87c7b70a --- /dev/null +++ b/libse/SubtitleFormats/CaptionsInc.cs @@ -0,0 +1,246 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class CaptionsInc : SubtitleFormat + { + public override string Extension + { + get { return ".cin"; } + } + + public override string Name + { + get { return "Caption Inc"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public static void Save(string fileName, Subtitle subtitle) + { + using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write)) + { + string name = Path.GetFileNameWithoutExtension(fileName) ?? string.Empty; + byte[] buffer = Encoding.ASCII.GetBytes(name); + for (int i = 0; i < buffer.Length && i < 8; i++) + fs.WriteByte(buffer[i]); + while (fs.Length < 8) + fs.WriteByte(0x20); + + WriteTime(fs, subtitle.Paragraphs[0].StartTime, false); // first start time + WriteTime(fs, subtitle.Paragraphs[subtitle.Paragraphs.Count - 1].EndTime, false); // last end time + + buffer = Encoding.ASCII.GetBytes("Generic Unknown Unknown \"\" Unknown Unknown Unknown "); + fs.Write(buffer, 0, buffer.Length); + + // paragraphs + foreach (Paragraph p in subtitle.Paragraphs) + { + buffer = new byte[] { 0x0D, 0x0A, 0xFE }; // header + fs.Write(buffer, 0, buffer.Length); + + // styles + var text = new List { 0x14, 0x20, 0x14, 0x2E, 0x14, 0x54, 0x17 }; + int noOfLines = Utilities.GetNumberOfLines(p.Text); + if (noOfLines == 1) + text.Add(0x22); // 1 line? + else + text.Add(0x21); // 2 lines? + + var lines = p.Text.Split(Utilities.NewLineChars, StringSplitOptions.None); + foreach (string line in lines) + { + foreach (char ch in line) + text.Add(Encoding.GetEncoding(1252).GetBytes(new[] { ch })[0]); + + // new line + //text.Add(0x14); // y? 0x14 was lower!? 0x17 is higher??? 12=little top 11=top, 13=most buttom?, 15=little over middle + //text.Add(0x72); + + text.Add(0x14); + text.Add(0x74); + //text.Add(0x17); + //text.Add(0x21); + } + + // codes+text length + buffer = Encoding.ASCII.GetBytes(string.Format("{0:000}", text.Count)); + fs.Write(buffer, 0, buffer.Length); + + WriteTime(fs, p.StartTime, true); + + // write codes + text + foreach (byte b in text) + fs.WriteByte(b); + + buffer = new byte[] { 0x14, 0x2F, 0x0D, 0x0A, 0xFE, 0x30, 0x30, 0x32, 0x30 }; + fs.Write(buffer, 0, buffer.Length); + WriteTime(fs, p.EndTime, true); + //buffer = new byte[] { 0x14, 0x2C }; + } + } + } + + private static void WriteTime(FileStream fs, TimeCode timeCode, bool addEndBytes) + { + var time = timeCode.ToHHMMSSFF(); + var buffer = Encoding.ASCII.GetBytes(time); + fs.Write(buffer, 0, buffer.Length); + if (addEndBytes) + { + fs.WriteByte(0xd); + fs.WriteByte(0xa); + } + } + + public override bool IsMine(List lines, string fileName) + { + if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName)) + { + if (!fileName.EndsWith(".cin", StringComparison.OrdinalIgnoreCase)) + return false; + + var sub = new Subtitle(); + LoadSubtitle(sub, lines, fileName); + return sub.Paragraphs.Count > 0; + } + return false; + } + + public override string ToText(Subtitle subtitle, string title) + { + return "Not supported!"; + } + + private static TimeCode DecodeTimestamp(string timeCode) + { + try + { + return new TimeCode(int.Parse(timeCode.Substring(0, 2)), int.Parse(timeCode.Substring(2, 2)), int.Parse(timeCode.Substring(4, 2)), FramesToMillisecondsMax999(int.Parse(timeCode.Substring(6, 2)))); + } + catch (Exception exception) + { + System.Diagnostics.Debug.WriteLine(exception.Message); + return new TimeCode(0, 0, 0, 0); + } + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + subtitle.Paragraphs.Clear(); + subtitle.Header = null; + byte[] buffer = FileUtil.ReadAllBytesShared(fileName); + + int i = 256; + Paragraph last = null; + while (i < buffer.Length - 20) + { + var p = new Paragraph(); + + while (buffer[i] != 0xfe && i < buffer.Length - 20) + { + i++; + } + if (buffer[i] == 0xfe) + { + i += 4; + string startTime = Encoding.ASCII.GetString(buffer, i, 8); + i += 8; + if (Utilities.IsInteger(startTime)) + { + p.StartTime = DecodeTimestamp(startTime); + } + } + + bool startFound = false; + bool textEnd = false; + while (!startFound && !textEnd && i < buffer.Length - 20) + { + bool skip = false; + if (buffer[i] == 0x0d) + i++; + else if (buffer[i] == 0x0a) + skip = true; + else if (buffer[i] == 0x14 && buffer[i + 1] == 0x2c) // text end + textEnd = true; + else if (buffer[i] <= 0x20) // text start + i++; + else + startFound = true; + + if (!skip) + i++; + } + i++; + + if (!textEnd) + { + i -= 2; + var sb = new StringBuilder(); + while (!textEnd && i < buffer.Length - 20) + { + if (buffer[i] == 0x14 && buffer[i + 1] == 0x2c) // text end + textEnd = true; + else if (buffer[i] == 0xd && buffer[i + 1] == 0xa) // text end + textEnd = true; + else if (buffer[i] <= 0x17) + { + if (!sb.ToString().EndsWith(Environment.NewLine)) + sb.Append(Environment.NewLine); + i++; + } + else + sb.Append(Encoding.GetEncoding(1252).GetString(buffer, i, 1)); + i++; + } + i++; + if (sb.Length > 0) + { + string text = sb.ToString().Trim(); + p.Text = text; + subtitle.Paragraphs.Add(p); + last = p; + } + } + + if (buffer[i] == 0xFE) + { + string endTime = Encoding.ASCII.GetString(buffer, i + 4, 8); + if (Utilities.IsInteger(endTime)) + { + p.EndTime = DecodeTimestamp(endTime); + } + while (i < buffer.Length && buffer[i] != 0xa) + i++; + i++; + } + else + { + while (i < buffer.Length && buffer[i] != 0xa) + i++; + i++; + + if (buffer[i] == 0xfe) + { + string endTime = Encoding.ASCII.GetString(buffer, i + 4, 8); + if (Utilities.IsInteger(endTime)) + { + p.EndTime = DecodeTimestamp(endTime); + } + } + } + } + if (last != null && last.Duration.TotalMilliseconds > Configuration.Settings.General.SubtitleMaximumDisplayMilliseconds) + last.EndTime.TotalMilliseconds = last.StartTime.TotalMilliseconds + Utilities.GetOptimalDisplayMilliseconds(last.Text); + + subtitle.Renumber(); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/CaraokeXml.cs b/libse/SubtitleFormats/CaraokeXml.cs new file mode 100644 index 000000000..38fd0448f --- /dev/null +++ b/libse/SubtitleFormats/CaraokeXml.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class CaraokeXml : SubtitleFormat + { + public override string Extension + { + get { return ".crk"; } + } + + public override string Name + { + get { return "Caraoke Xml"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > 0; + } + + public override string ToText(Subtitle subtitle, string title) + { + string xmlStructure = + "" + Environment.NewLine + + ""; + + var xml = new XmlDocument(); + xml.LoadXml(xmlStructure); + var paragraph = xml.DocumentElement.SelectSingleNode("paragraph"); + foreach (Paragraph p in subtitle.Paragraphs) + { + XmlNode item = xml.CreateElement("item"); + + var start = xml.CreateAttribute("tc1"); + start.InnerText = p.StartTime.TotalMilliseconds.ToString(System.Globalization.CultureInfo.InvariantCulture); + item.Attributes.Append(start); + + var end = xml.CreateAttribute("tc2"); + end.InnerText = p.EndTime.TotalMilliseconds.ToString(System.Globalization.CultureInfo.InvariantCulture); + item.Attributes.Append(end); + + var attr = xml.CreateAttribute("attr"); + attr.InnerText = string.Empty; + item.Attributes.Append(attr); + + item.InnerText = p.Text; + + paragraph.AppendChild(item); + } + + return ToUtf8XmlString(xml); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + + string xmlAsText = sb.ToString(); + + if (!xmlAsText.Contains(" HebrewCodes = new List { + 0x40, // א + 0x41, // ב + 0x42, // ג + 0x43, // ד + 0x44, // ה + 0x45, // ו + 0x46, // ז + 0x47, // ח + 0x49, // י + 0x4c, // ל + 0x4d, // ם + 0x4e, // מ + 0x4f, // ן + 0x50, // נ + 0x51, // ס + 0x52, // ע + 0x54, // פ + 0x56, // צ + 0x57, // ק + 0x58, // ר + 0x59, // ש + 0x5A, // ת + 0x4b, // כ + 0x4a, // ך + 0x48, // ט + 0x53, // ף + 0x55, // ץ + }; + + private static readonly List HebrewLetters = new List { + "א", + "ב", + "ג", + "ד", + "ה", + "ו", + "ז", + "ח", + "י", + "ל", + "ם", + "מ", + "ן", + "נ", + "ס", + "ע", + "פ", + "צ", + "ק", + "ר", + "ש", + "ת", + "כ", + "ך", + "ט", + "ף", + "ץ", + }; + + private static readonly List RussianCodes = new List { + 0x42, // Б + 0x45, // Е + 0x5A, // З + 0x56, // В + 0x49, // И + 0x4E, // Н + 0x58, // Ы + 0x51, // Я + 0x56, // V + 0x53, // С + 0x72, // р + 0x69, // и + 0x71, // я + 0x6E, // н + 0x74, // т + 0x5C, // Э + 0x77, // ю + 0x46, // Ф + 0x5E, // Ч + 0x44, // Д + 0x62, // б + 0x73, // с + 0x75, // у + 0x64, // д + 0x60, // ж + 0x6A, // й + 0x6C, // л + 0x47, // Г + 0x78, // ы + 0x7A, // з + 0x7E, // ч + 0x6D, // м + 0x67, // г + 0x79, // ь + 0x70, // п + 0x76, // в + 0x55, // У + 0x7D, // щ + 0x66, // ф + 0x7C, // э + 0x7B, // ш + 0x50, // П + 0x52, // П + 0x68, // П + }; + + private static readonly List RussianLetters = new List { + "Б", + "Е", + "З", + "В", + "И", + "Н", + "Ы", + "Я", + "V", + "С", + "р", + "и", + "я", + "н", + "т", + "Э", + "ю", + "Ф", + "Ч", + "Д", + "б", + "с", + "у", + "д", + "ж", + "й", + "л", + "Г", + "ы", + "з", + "ч", + "м", + "г", + "ь", + "п", + "в", + "У", + "щ", + "ф", + "э", + "ш", + "П", + "Р", + "х", + }; + + public override string Extension + { + get { return ".890"; } + } + + public const string NameOfFormat = "Cavena 890"; + + public override string Name + { + get { return NameOfFormat; } + } + + public override bool IsTimeBased + { + get { return false; } + } + + private int _languageIdLine1 = LanguageIdEnglish; + private int _languageIdLine2 = LanguageIdEnglish; + + public void Save(string fileName, Subtitle subtitle) + { + using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write)) + { + int russianCount = 0; + foreach (Paragraph p in subtitle.Paragraphs) + { + if (p.Text.Contains(new[] { '的', '是', '啊', '吧', '好', '吧', '亲', '爱', '的', '早', '上' })) + { + _languageIdLine1 = LanguageIdChineseSimplified; + _languageIdLine2 = LanguageIdChineseSimplified; + break; + } + if (p.Text.Contains(new[] { 'я', 'д', 'й', 'л', 'щ', 'ж', 'ц', 'ф', 'ы' })) + { + russianCount++; + if (russianCount > 10) + { + _languageIdLine1 = LanguageIdRussian; + _languageIdLine2 = LanguageIdRussian; // or 0x09? + break; + } + } + } + + var language = Utilities.AutoDetectGoogleLanguage(subtitle); + if (language == "he") // Hebrew + { + _languageIdLine1 = LanguageIdHebrew; + _languageIdLine2 = LanguageIdHebrew; // or 0x09 + } + else if (language == "ru") + { + _languageIdLine1 = LanguageIdRussian; + _languageIdLine2 = LanguageIdRussian; // or 0x09? + } + else if (language == "zh") + { + _languageIdLine1 = LanguageIdChineseSimplified; + _languageIdLine2 = LanguageIdChineseSimplified; + } + else if (language == "da") + { + _languageIdLine1 = LanguageIdDanish; + _languageIdLine2 = LanguageIdDanish; + } + + // prompt??? + //if (Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine1 >= 0) + // _languageIdLine1 = Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine1; + //if (Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine2 >= 0) + // _languageIdLine2 = Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine2; + + // write file header (some fields are known, some are not...) + + fs.WriteByte(0); // ? + fs.WriteByte(0); // ? + + // tape number (20 bytes) + for (int i = 0; i < 20; i++) + fs.WriteByte(0); + + // ? + for (int i = 0; i < 18; i++) + fs.WriteByte(0); + + // translated programme title (28 bytes) + string title = Path.GetFileNameWithoutExtension(fileName) ?? string.Empty; + if (title.Length > 28) + title = title.Substring(0, 28); + var buffer = Encoding.ASCII.GetBytes(title); + fs.Write(buffer, 0, buffer.Length); + for (int i = 0; i < 28 - buffer.Length; i++) + fs.WriteByte(0); + + // translator (28 bytes) + for (int i = 0; i < 28; i++) + fs.WriteByte(0); + + // ? + for (int i = 0; i < 9; i++) + fs.WriteByte(0); + + // translated episode title (11 bytes) + for (int i = 0; i < 11; i++) + fs.WriteByte(0); + + // ? + for (int i = 0; i < 18; i++) + fs.WriteByte(0); + + // ? + language codes + buffer = new byte[] { 0xA0, 0x05, 0x04, 0x03, 0x06, 0x06, 0x08, 0x90, 0x00, 0x00, 0x00, 0x00, (byte)_languageIdLine1, (byte)_languageIdLine2 }; + fs.Write(buffer, 0, buffer.Length); + + // comments (24 bytes) + buffer = Encoding.ASCII.GetBytes("Made with Subtitle Edit"); + fs.Write(buffer, 0, buffer.Length); + for (int i = 0; i < 24 - buffer.Length; i++) + fs.WriteByte(0); + + // ?? + buffer = new byte[] { 0x08, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00 }; + fs.Write(buffer, 0, buffer.Length); + + // number of subtitles + fs.WriteByte((byte)(subtitle.Paragraphs.Count % 256)); + fs.WriteByte((byte)(subtitle.Paragraphs.Count / 256)); + + // write font - prefix with binary zeroes + buffer = GetFontBytesFromLanguageId(_languageIdLine1); // also TBX308VFONTL.V for english... + for (int i = 0; i < 14 - buffer.Length; i++) + fs.WriteByte(0); + fs.Write(buffer, 0, buffer.Length); + + // ? + for (int i = 0; i < 13; i++) + fs.WriteByte(0); + + // some language codes again? + if (_languageIdLine1 == LanguageIdHebrew || _languageIdLine2 == LanguageIdHebrew) + { + buffer = new byte[] { 0x64, 0x02, 0x64, 0x02 }; + } + else if (_languageIdLine1 == LanguageIdRussian || _languageIdLine2 == LanguageIdRussian) + { + buffer = new byte[] { 0xce, 0x00, 0xce, 0x00 }; + } + else + { + buffer = new byte[] { 0x37, 0x00, 0x37, 0x00 }; // seen in English files + } + fs.Write(buffer, 0, buffer.Length); + + // ? + for (int i = 0; i < 6; i++) + fs.WriteByte(0); + + // original programme title (28 chars) + for (int i = 0; i < 28; i++) + fs.WriteByte(0); + + // write font (use same font id from line 1) + buffer = GetFontBytesFromLanguageId(_languageIdLine1); + fs.Write(buffer, 0, buffer.Length); + + buffer = new byte[] + { + 0x3d, 0x8d, 0x31, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x54, 0x44 + }; + fs.Write(buffer, 0, buffer.Length); + + for (int i = 0; i < 92; i++) + fs.WriteByte(0); + + // paragraphs + int number = 16; + foreach (Paragraph p in subtitle.Paragraphs) + { + // number + fs.WriteByte((byte)(number / 256)); + fs.WriteByte((byte)(number % 256)); + + WriteTime(fs, p.StartTime); + WriteTime(fs, p.EndTime); + + if (p.Text.StartsWith("{\\an1}")) + fs.WriteByte(0x50); // left + else if (p.Text.StartsWith("{\\an3}")) + fs.WriteByte(0x52); // left + else + fs.WriteByte(0x54); // center + + buffer = new byte[] { 0, 0, 0, 0, 0, 0, 0 }; // 0x16 }; -- the last two bytes might be something with vertical alignment... + fs.Write(buffer, 0, buffer.Length); + + WriteText(fs, p.Text, p == subtitle.Paragraphs[subtitle.Paragraphs.Count - 1], _languageIdLine1); + + number += 16; + } + } + } + + private static byte[] GetFontBytesFromLanguageId(int languageId) + { + var buffer = Encoding.ASCII.GetBytes("HLV23N.V"); + if (languageId == LanguageIdChineseTraditional || languageId == LanguageIdChineseSimplified) + buffer = Encoding.ASCII.GetBytes("CCKM44.V"); + else if (languageId == LanguageIdArabic) + buffer = Encoding.ASCII.GetBytes("ARA19N.V"); + else if (languageId == LanguageIdRussian) + buffer = Encoding.ASCII.GetBytes("KYRIL4.V"); + else if (languageId == LanguageIdHebrew) + buffer = Encoding.ASCII.GetBytes("HEBNOA.V"); + else if (languageId == LanguageIdDanish) + buffer = Encoding.ASCII.GetBytes("VFONTL.V"); + return buffer; + } + + private static void WriteText(FileStream fs, string text, bool isLast, int languageIdLine) + { + string line1 = string.Empty; + string line2 = string.Empty; + var lines = text.SplitToLines(); + if (lines.Length > 2) + lines = Utilities.AutoBreakLine(text).SplitToLines(); + if (lines.Length > 1) + { + line1 = lines[0]; + line2 = lines[1]; + } + else if (lines.Length == 1) + { + line2 = lines[0]; + } + + var buffer = GetTextAsBytes(line1, languageIdLine); + fs.Write(buffer, 0, buffer.Length); + + buffer = new byte[] { 00, 00, 00, 00, 00, 00 }; + fs.Write(buffer, 0, buffer.Length); + + buffer = GetTextAsBytes(line2, languageIdLine); + fs.Write(buffer, 0, buffer.Length); + + buffer = new byte[] { 00, 00, 00, 00 }; + if (!isLast) + fs.Write(buffer, 0, buffer.Length); + } + + private static byte[] GetTextAsBytes(string text, int languageId) + { + var buffer = new byte[51]; + int skipCount = 0; + for (int i = 0; i < buffer.Length; i++) + buffer[i] = 0x7F; + + if (languageId == LanguageIdChineseTraditional || languageId == LanguageIdChineseSimplified) + { + for (int i = 0; i < buffer.Length; i++) + buffer[i] = 0; + } + + var encoding = Encoding.Default; + int index = 0; + for (int i = 0; i < text.Length; i++) + { + var current = text[i]; + if (skipCount > 0) + { + skipCount--; + } + else if (languageId == LanguageIdHebrew) + { + int letterIndex = HebrewLetters.IndexOf(current.ToString(CultureInfo.InvariantCulture)); + if (letterIndex >= 0) + { + buffer[index] = (byte)HebrewCodes[letterIndex]; + } + else if (i + 3 < text.Length && text.Substring(i, 3) == "") + { + buffer[index] = 0x88; + skipCount = 2; + } + else if (i + 4 <= text.Length && text.Substring(i, 4) == "") + { + buffer[index] = 0x98; + skipCount = 2; + } + else + { + buffer[index] = encoding.GetBytes(new[] { current })[0]; + } + index++; + } + else if (languageId == LanguageIdChineseTraditional || languageId == LanguageIdChineseSimplified) + { + encoding = Encoding.GetEncoding(1201); + if (index < 49) + { + if (i + 3 < text.Length && text.Substring(i, 3) == "") + { + buffer[index] = 0x88; + skipCount = 2; + } + else if (i + 4 <= text.Length && text.Substring(i, 4) == "") + { + buffer[index] = 0x98; + skipCount = 3; + } + else + { + buffer[index] = encoding.GetBytes(new[] { current })[0]; + index++; + } + } + } + else + { + if (index < 50) + { + if (current == 'æ') + buffer[index] = 0x1B; + else if (current == 'ø') + buffer[index] = 0x1C; + else if (current == 'å') + buffer[index] = 0x1D; + else if (current == 'Æ') + buffer[index] = 0x5B; + else if (current == 'Ø') + buffer[index] = 0x5C; + else if (current == 'Å') + buffer[index] = 0x5D; + else if (current == 'Ä') + { + buffer[index] = 0x86; + index++; + buffer[index] = 0x41; + } + else if (current == 'ä') + { + buffer[index] = 0x86; + index++; + buffer[index] = 0x61; + } + else if (current == 'Ö') + { + buffer[index] = 0x86; + index++; + buffer[index] = 0x4F; + } + else if (current == 'ö') + { + buffer[index] = 0x86; + index++; + buffer[index] = 0x6F; + } + + // different language setting? + //else if (current == 'å') + //{ + // buffer[index] = 0x8C; + // index++; + // buffer[index] = 0x61; + //} + //else if (current == 'Å') + //{ + // buffer[index] = 0x8C; + // index++; + // buffer[index] = 0x41; + //} + + // ăĂ îÎ şŞ ţŢ â (romanian) + else if (current == 'ă') + { + buffer[index] = 0x89; + index++; + buffer[index] = 0x61; + } + else if (current == 'Ă') + { + buffer[index] = 0x89; + index++; + buffer[index] = 0x41; + } + else if (current == 'î') + { + buffer[index] = 0x83; + index++; + buffer[index] = 0x69; + } + else if (current == 'Î') + { + buffer[index] = 0x83; + index++; + buffer[index] = 0x49; + } + else if (current == 'ş') + { + buffer[index] = 0x87; + index++; + buffer[index] = 0x73; + } + else if (current == 'Ş') + { + buffer[index] = 0x87; + index++; + buffer[index] = 0x53; + } + else if (current == 'ţ') + { + buffer[index] = 0x87; + index++; + buffer[index] = 0x74; + } + else if (current == 'Ţ') + { + buffer[index] = 0x87; + index++; + buffer[index] = 0x74; + } + else if (current == 'â') + { + buffer[index] = 0x83; + index++; + buffer[index] = 0x61; + } + else if (current == 'Â') + { + buffer[index] = 0x83; + index++; + buffer[index] = 0x41; + } + else if (current == 'è') + { + buffer[index] = 0x81; + index++; + buffer[index] = 0x65; + } + else if (current == 'é') + { + buffer[index] = 0x82; + index++; + buffer[index] = 0x65; + } + else if (current == 'É') + { + buffer[index] = 0x82; + index++; + buffer[index] = 0x45; + } + else if (current == 'È') + { + buffer[index] = 0x81; + index++; + buffer[index] = 0x45; + } + else if (i + 3 < text.Length && text.Substring(i, 3) == "") + { + buffer[index] = 0x88; + skipCount = 2; + } + else if (i + 4 <= text.Length && text.Substring(i, 4) == "") + { + buffer[index] = 0x98; + skipCount = 3; + } + else + { + buffer[index] = encoding.GetBytes(new[] { current })[0]; + } + index++; + } + } + } + + return buffer; + } + + private static void WriteTime(FileStream fs, TimeCode timeCode) + { + double totalMilliseconds = timeCode.TotalMilliseconds; + int frames = (int)Math.Round(totalMilliseconds / (TimeCode.BaseUnit / Configuration.Settings.General.CurrentFrameRate)); + fs.WriteByte((byte)(frames / 256 / 256)); + fs.WriteByte((byte)(frames / 256)); + fs.WriteByte((byte)(frames % 256)); + } + + public override bool IsMine(List lines, string fileName) + { + if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName)) + { + var fi = new FileInfo(fileName); + if (fi.Length >= 640 && fi.Length < 1024000) // not too small or too big + { + if (!fileName.EndsWith(".890", StringComparison.Ordinal)) + return false; + + var sub = new Subtitle(); + LoadSubtitle(sub, lines, fileName); + return sub.Paragraphs.Count > 0; + } + } + return false; + } + + public override string ToText(Subtitle subtitle, string title) + { + return "Not supported!"; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + const int textLength = 51; + + subtitle.Paragraphs.Clear(); + subtitle.Header = null; + byte[] buffer = FileUtil.ReadAllBytesShared(fileName); + + _languageIdLine1 = buffer[146]; + if (_languageIdLine1 == 0) + _languageIdLine1 = LanguageIdEnglish; + Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine1 = _languageIdLine1; + + _languageIdLine2 = buffer[147]; + if (_languageIdLine2 == 0) + _languageIdLine2 = LanguageIdEnglish; + Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine2 = _languageIdLine2; + + var fontNameLine1 = Encoding.ASCII.GetString(buffer, 187, 6); + var fontNameLine2 = Encoding.ASCII.GetString(buffer, 246, 6); + + // Hebrew + if (_languageIdLine1 == LanguageIdHebrew || fontNameLine1 == "HEBNOA" || fontNameLine2 == "HEBNOA") + { + _languageIdLine1 = LanguageIdHebrew; + _languageIdLine2 = LanguageIdHebrew; + } + + // Russian + else if (_languageIdLine1 == LanguageIdRussian || fontNameLine1.StartsWith("KYRIL", StringComparison.Ordinal) || fontNameLine2.StartsWith("KYRIL", StringComparison.Ordinal)) + { + _languageIdLine1 = LanguageIdRussian; + _languageIdLine2 = LanguageIdRussian; + } + + + // Chinese + else if (_languageIdLine1 == LanguageIdChineseSimplified) + { + _languageIdLine1 = LanguageIdChineseSimplified; + _languageIdLine2 = LanguageIdChineseSimplified; + } + else if (_languageIdLine1 == LanguageIdChineseTraditional || fontNameLine1 == "CCKM44" || fontNameLine2 == "CCKM44") + { + _languageIdLine1 = LanguageIdChineseTraditional; + _languageIdLine2 = LanguageIdChineseTraditional; + } + + + int i = 455; + int lastNumber = -1; + while (i < buffer.Length - 20) + { + int start = i - textLength; + + int number = buffer[start - 16] * 256 + buffer[start - 15]; + + var p = new Paragraph(); + double startFrame = buffer[start - 14] * 256 * 256 + buffer[start - 13] * 256 + buffer[start - 12]; + double endFrame = buffer[start - 11] * 256 * 256 + buffer[start - 10] * 256 + buffer[start - 9]; + + string line1 = FixText(buffer, start, textLength, _languageIdLine1); + string line2 = FixText(buffer, start + textLength + 6, textLength, _languageIdLine2); + + if (lastNumber == number) + { + p = subtitle.Paragraphs[subtitle.Paragraphs.Count - 1]; + string temp = (line1 + Environment.NewLine + line2).Trim(); + if (temp.Length > 0) + p.Text = temp; + } + else + { + subtitle.Paragraphs.Add(p); + p.StartTime.TotalMilliseconds = (TimeCode.BaseUnit / Configuration.Settings.General.CurrentFrameRate) * startFrame; + p.EndTime.TotalMilliseconds = (TimeCode.BaseUnit / Configuration.Settings.General.CurrentFrameRate) * endFrame; + p.Text = (line1 + Environment.NewLine + line2).Trim(); + } + + lastNumber = number; + + i += 128; + } + + subtitle.Renumber(); + } + + private static string FixText(byte[] buffer, int start, int textLength, int languageId) + { + string text; + + if (languageId == LanguageIdRussian) + { + var encoding = Encoding.Default; // which encoding?? Encoding.GetEncoding("ISO-8859-5") + var sb = new StringBuilder(); + for (int i = 0; i < textLength; i++) + { + int b = buffer[start + i]; + int idx = RussianCodes.IndexOf(b); + if (idx >= 0) + sb.Append(RussianLetters[idx]); + else + sb.Append(encoding.GetString(buffer, start + i, 1)); + } + + text = sb.ToString(); + + text = text.Replace(encoding.GetString(new byte[] { 0x7F }), string.Empty); // Used to fill empty space upto 51 bytes + text = text.Replace(encoding.GetString(new byte[] { 0xBE }), string.Empty); // Unknown? + + if (text.Contains("")) + text = text.Replace("", ""); + if (text.Contains("") && !text.Contains("")) + text += ""; + } + else if (languageId == LanguageIdHebrew) // (_language == "HEBNOA") + { + var encoding = Encoding.Default; // which encoding?? Encoding.GetEncoding("ISO-8859-5") + var sb = new StringBuilder(); + for (int i = 0; i < textLength; i++) + { + int b = buffer[start + i]; + int idx = HebrewCodes.IndexOf(b); + if (idx >= 0) + sb.Append(HebrewLetters[idx]); + else + sb.Append(encoding.GetString(buffer, start + i, 1)); + } + + text = sb.ToString(); + + text = text.Replace(encoding.GetString(new byte[] { 0x7F }), string.Empty); // Used to fill empty space upto 51 bytes + text = text.Replace(encoding.GetString(new byte[] { 0xBE }), string.Empty); // Unknown? + + text = Utilities.FixEnglishTextInRightToLeftLanguage(text, ",.?-'/\"0123456789abcdefghijklmnopqrstuvwxyz"); + } + else if (languageId == LanguageIdChineseTraditional || languageId == LanguageIdChineseSimplified) // (_language == "CCKM44" || _language == "TVB000") + { + int index = start; + + while (textLength >= 1 && index + textLength < buffer.Length && (buffer[index + textLength - 1] == 0)) + textLength--; + if (textLength > 0) + { + text = Encoding.GetEncoding(1201).GetString(buffer, index, textLength).Replace("\0", string.Empty); + } + else + { + text = string.Empty; + } + + var encoding = Encoding.Default; // which encoding?? Encoding.GetEncoding("ISO-8859-5") + text = text.Replace(encoding.GetString(new byte[] { 0x7F }), string.Empty); // Used to fill empty space upto 51 bytes + text = text.Replace(encoding.GetString(new byte[] { 0xBE }), string.Empty); // Unknown? + + text = text.Replace(encoding.GetString(new byte[] { 0x88 }), ""); + text = text.Replace(encoding.GetString(new byte[] { 0x98 }), ""); + + if (text.Contains("")) + text = text.Replace("", ""); + if (text.Contains("") && !text.Contains("")) + text += ""; + } + else + { + var encoding = Encoding.Default; // which encoding?? Encoding.GetEncoding("ISO-8859-5") + text = encoding.GetString(buffer, start, textLength).Replace("\0", string.Empty); + + text = text.Replace(encoding.GetString(new byte[] { 0x7F }), string.Empty); // Used to fill empty space upto 51 bytes + text = text.Replace(encoding.GetString(new byte[] { 0xBE }), string.Empty); // Unknown? + text = text.Replace(encoding.GetString(new byte[] { 0x1B }), "æ"); + text = text.Replace(encoding.GetString(new byte[] { 0x1C }), "ø"); + text = text.Replace(encoding.GetString(new byte[] { 0x1D }), "å"); + text = text.Replace(encoding.GetString(new byte[] { 0x1E }), "Æ"); + text = text.Replace(encoding.GetString(new byte[] { 0x1F }), "Ø"); + + text = text.Replace(encoding.GetString(new byte[] { 0x5B }), "Æ"); + text = text.Replace(encoding.GetString(new byte[] { 0x5C }), "Ø"); + text = text.Replace(encoding.GetString(new byte[] { 0x5D }), "Å"); + + text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x41 }), "Ä"); + text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x61 }), "ä"); + text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x4F }), "Ö"); + text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x6F }), "ö"); + + text = text.Replace(encoding.GetString(new byte[] { 0x8C, 0x61 }), "å"); + text = text.Replace(encoding.GetString(new byte[] { 0x8C, 0x41 }), "Å"); + + text = text.Replace(encoding.GetString(new byte[] { 0x81, 0x65 }), "è"); + text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x65 }), "é"); + + text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x45 }), "É"); + text = text.Replace(encoding.GetString(new byte[] { 0x81, 0x65 }), "È"); + + text = text.Replace(encoding.GetString(new byte[] { 0x88 }), ""); + text = text.Replace(encoding.GetString(new byte[] { 0x98 }), ""); + + //ăĂ îÎ şŞ ţŢ â (romanian) + text = text.Replace(encoding.GetString(new byte[] { 0x89, 0x61 }), "ă"); + text = text.Replace(encoding.GetString(new byte[] { 0x89, 0x41 }), "Ă"); + text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x69 }), "î"); + text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x49 }), "Î"); + text = text.Replace(encoding.GetString(new byte[] { 0x87, 0x73 }), "ş"); + text = text.Replace(encoding.GetString(new byte[] { 0x87, 0x53 }), "Ş"); + text = text.Replace(encoding.GetString(new byte[] { 0x87, 0x74 }), "ţ"); + text = text.Replace(encoding.GetString(new byte[] { 0x87, 0x54 }), "Ţ"); + text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x61 }), "â"); + text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x41 }), "Â"); + + if (text.Contains("")) + text = text.Replace("", ""); + if (text.Contains("") && !text.Contains("")) + text += ""; + } + return text; + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/CheetahCaption.cs b/libse/SubtitleFormats/CheetahCaption.cs new file mode 100644 index 000000000..4e329dac7 --- /dev/null +++ b/libse/SubtitleFormats/CheetahCaption.cs @@ -0,0 +1,357 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class CheetahCaption : SubtitleFormat + { + private static readonly List LatinCodes = new List { + 0x81, // ♪ + 0x82, // á + 0x83, // é + 0x84, // í + 0x85, // ó + 0x86, // ú + 0x87, // â + 0x88, // ê + 0x89, // î + 0x8A, // ô + 0x8B, // û + 0x8C, // à + 0x8D, // è + 0x8E, // Ñ + 0x8F, // ñ + 0x90, // ç + 0x91, // ¢ + 0x92, // £ + 0x93, // ¿ + 0x94, // ½ + 0x95, // ® + }; + + private static readonly IList LatinLetters = new List { + '♪', + 'á', + 'é', + 'í', + 'ó', + 'ú', + 'â', + 'ê', + 'î', + 'ô', + 'û', + 'à', + 'è', + 'Ñ', + 'ñ', + 'ç', + '¢', + '£', + '¿', + '½', + '®', + }; + + public override string Extension + { + get { return ".cap"; } + } + + public const string NameOfFormat = "Cheetah Caption"; + + public override string Name + { + get { return NameOfFormat; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public static void Save(string fileName, Subtitle subtitle) + { + using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write)) + { + byte[] buffer = { 0xEA, 0x22, 1, 0 }; // header + fs.Write(buffer, 0, buffer.Length); + + int numberOfLines = subtitle.Paragraphs.Count; + fs.WriteByte((byte)(numberOfLines % 256)); // paragraphs - low byte + fs.WriteByte((byte)(numberOfLines / 256)); // paragraphs - high byte + + buffer = new byte[] { 9, 0xA8, 0xAF, 0x4F }; // ? + fs.Write(buffer, 0, buffer.Length); + + for (int i = 0; i < 118; i++) + fs.WriteByte(0); + + // paragraphs + for (int index = 0; index < subtitle.Paragraphs.Count; index++) + { + Paragraph p = subtitle.Paragraphs[index]; + Paragraph next = subtitle.GetParagraphOrDefault(index + 1); + string text = p.Text; + + var bufferShort = new byte[] + { + 0, + 0, + 3, // justification, 1=left, 2=right, 3=center + 0xE, //horizontal position, 1=top, F=bottom + 0x10 //horizontal position, 3=left, 0x10=center, 0x19=right + }; + + //styles + ? + buffer = new byte[] + { + 0x12, + 1, + 0, + 0, + 0, + 0, + 3, // justification, 1=left, 2=right, 3=center + 0xF, //horizontal position, 1=top, F=bottom + 0x10 //horizontal position, 3=left, 0x10=center, 0x19=right + }; + + //Normal : 12 01 00 00 00 00 03 0F 10 + //Right-top : 12 01 00 00 00 00 03 01 1C + //Top : 12 01 00 00 00 00 03 01 10 + //Left-top : 12 01 00 00 00 00 03 01 05 + //Left : 12 01 00 00 00 00 03 0F 0A + //Right : 12 01 00 00 00 00 03 0F 1E + //Left : 12 03 00 00 00 00 03 0F 07 + + if (text.StartsWith("{\\an7}", StringComparison.Ordinal) || text.StartsWith("{\\an8}", StringComparison.Ordinal) || text.StartsWith("{\\an9}", StringComparison.Ordinal)) + { + buffer[7] = 1; // align top (vertial) + bufferShort[3] = 1; // align top (vertial) + } + else if (text.StartsWith("{\\an4}", StringComparison.Ordinal) || text.StartsWith("{\\an5}", StringComparison.Ordinal) || text.StartsWith("{\\an6}", StringComparison.Ordinal)) + { + buffer[7] = 8; // center (vertical) + bufferShort[3] = 8; // align top (vertial) + } + + if (text.StartsWith("{\\an7}", StringComparison.Ordinal) || text.StartsWith("{\\an4}", StringComparison.Ordinal) || text.StartsWith("{\\an1}", StringComparison.Ordinal)) + { + buffer[8] = 2; // align left (horizontal) + bufferShort[4] = 2; // align left (horizontal) + } + else if (text.StartsWith("{\\an9}", StringComparison.Ordinal) || text.StartsWith("{\\an6}", StringComparison.Ordinal) || text.StartsWith("{\\an3}", StringComparison.Ordinal)) + { + buffer[8] = 0x1e; // align right (vertical) + bufferShort[4] = 0x1e; // align right (vertical) + } + + int startTag = text.IndexOf('}'); + if (text.StartsWith("{\\", StringComparison.Ordinal) && startTag > 0 && startTag < 10) + { + text = text.Remove(0, startTag + 1); + } + + var textBytes = new List(); + var italic = p.Text.StartsWith("", StringComparison.Ordinal) && p.Text.EndsWith("", StringComparison.Ordinal); + text = HtmlUtil.RemoveHtmlTags(text); + int j = 0; + if (italic) + textBytes.Add(0xd0); + + var encoding = Encoding.GetEncoding(1252); + while (j < text.Length) + { + if (text.Substring(j).StartsWith(Environment.NewLine)) + { + j += Environment.NewLine.Length; + textBytes.Add(0); + textBytes.Add(0); + textBytes.Add(0); + textBytes.Add(0); + if (italic) + textBytes.Add(0xd0); + } + else + { + int idx = LatinLetters.IndexOf(text[j]); + if (idx >= 0) + textBytes.Add((byte)LatinCodes[idx]); + else + textBytes.Add(encoding.GetBytes(new[] { text[j] })[0]); + + j++; + } + } + + int length = textBytes.Count + 20; + long end = fs.Position + length; + if (Configuration.Settings.SubtitleSettings.CheetahCaptionAlwayWriteEndTime || (next != null && next.StartTime.TotalMilliseconds - p.EndTime.TotalMilliseconds >= 1500)) + { + fs.WriteByte((byte)(length)); + + if (p.Text.Trim().Contains(Environment.NewLine)) + fs.WriteByte(0x62); // two lines? + else + fs.WriteByte(0x61); // one line? + + WriteTime(fs, p.StartTime); + WriteTime(fs, p.EndTime); + fs.Write(buffer, 0, buffer.Length); // styles + } + else + { + length = textBytes.Count + 20 - (buffer.Length - bufferShort.Length); + end = fs.Position + length; + fs.WriteByte((byte)(length)); + + if (p.Text.Trim().Contains(Environment.NewLine)) + fs.WriteByte(0x42); // two lines? + else + fs.WriteByte(0x41); // one line? + WriteTime(fs, p.StartTime); + fs.WriteByte(2); + fs.WriteByte(1); + fs.WriteByte(0); + fs.WriteByte(0); + fs.Write(bufferShort, 0, bufferShort.Length); // styles + } + + foreach (byte b in textBytes) // text + fs.WriteByte(b); + + while (end > fs.Position) + fs.WriteByte(0); + } + } + } + + private static void WriteTime(FileStream fs, TimeCode timeCode) + { + fs.WriteByte((byte)timeCode.Hours); + fs.WriteByte((byte)timeCode.Minutes); + fs.WriteByte((byte)timeCode.Seconds); + fs.WriteByte((byte)MillisecondsToFramesMaxFrameRate(timeCode.Milliseconds)); + } + + public override bool IsMine(List lines, string fileName) + { + if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName)) + { + var fi = new FileInfo(fileName); + if (fi.Length >= 200 && fi.Length < 1024000) // not too small or too big + { + if (fileName.EndsWith(".cap", StringComparison.OrdinalIgnoreCase)) + { + byte[] buffer = FileUtil.ReadAllBytesShared(fileName); + for (int i = 0; i < buffer.Length - 20; i++) + { + if (buffer[i + 0] == 0xEA && + buffer[i + 1] == 0x22 && + buffer[i + 2] <= 3) + return true; + } + } + } + } + return false; + } + + public override string ToText(Subtitle subtitle, string title) + { + return "Not supported!"; + } + + private static TimeCode DecodeTimestamp(byte[] buffer, int index) + { + return new TimeCode(buffer[index], buffer[index + 1], buffer[index + 2], FramesToMillisecondsMax999(buffer[index + 3])); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + subtitle.Paragraphs.Clear(); + subtitle.Header = null; + byte[] buffer = FileUtil.ReadAllBytesShared(fileName); + + int i = 128; + Paragraph last = null; + var sb = new StringBuilder(); + while (i < buffer.Length - 20) + { + var p = new Paragraph(); + int length = buffer[i]; + int textLength = length - 20; + int start = 19; + for (int j = 0; j < 4; j++) + { + if (buffer[i + start - 1] > 0x10) + { + start--; + textLength++; + } + } + if (textLength > 0 && buffer.Length >= i + textLength) + { + p.StartTime = DecodeTimestamp(buffer, i + 2); + + if (last != null && last.EndTime.TotalMilliseconds > p.StartTime.TotalMilliseconds) + last.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds - Configuration.Settings.General.MinimumMillisecondsBetweenLines; + + p.EndTime = DecodeTimestamp(buffer, i + 6); + + sb.Clear(); + int j = 0; + bool italics = false; + var encoding = Encoding.GetEncoding(1252); + while (j < textLength) + { + int index = i + start + j; + if (buffer[index] == 0) + { + if (italics) + sb.Append(""); + italics = false; + if (!sb.ToString().EndsWith(Environment.NewLine)) + sb.AppendLine(); + } + else if (LatinCodes.Contains(buffer[index])) + { + sb.Append(LatinLetters[LatinCodes.IndexOf(buffer[index])]); + } + else if (buffer[index] >= 0xC0 || buffer[index] <= 0x14) // codes/styles? + { + if (buffer[index] == 0xd0) // italics + { + italics = true; + sb.Append(""); + } + } + else + { + sb.Append(encoding.GetString(buffer, index, 1)); + } + j++; + } + if (italics) + sb.Append(""); + p.Text = sb.ToString().Trim(); + p.Text = p.Text.Replace("" + Environment.NewLine + "", Environment.NewLine); + + subtitle.Paragraphs.Add(p); + last = p; + } + if (length == 0) + length++; + i += length; + } + if (last != null && last.Duration.TotalMilliseconds > Configuration.Settings.General.SubtitleMaximumDisplayMilliseconds) + last.EndTime.TotalMilliseconds = last.StartTime.TotalMilliseconds + Utilities.GetOptimalDisplayMilliseconds(last.Text); + + subtitle.Renumber(); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/Chk.cs b/libse/SubtitleFormats/Chk.cs new file mode 100644 index 000000000..ad977f332 --- /dev/null +++ b/libse/SubtitleFormats/Chk.cs @@ -0,0 +1,257 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + /// + /// .CHK subtitle file format - 128 bytes blocks, first byte in block is id (01==text) + /// + public class Chk : SubtitleFormat + { + private readonly Encoding _codePage = Encoding.GetEncoding(850); + // private string _languageId = "DEN"; // English + + public override string Extension + { + get { return ".chk"; } + } + + public const string NameOfFormat = "CHK"; + + public override string Name + { + get { return NameOfFormat; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + if (fileName.EndsWith(".chk", StringComparison.OrdinalIgnoreCase)) + { + var buffer = FileUtil.ReadAllBytesShared(fileName); + return buffer.Length > 0 && buffer[0] == 0x1d; + } + return false; + } + + public override string ToText(Subtitle subtitle, string title) + { + return "Not implemented!"; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + var buffer = FileUtil.ReadAllBytesShared(fileName); + int index = 256; + _errorCount = 0; + subtitle.Paragraphs.Clear(); + while (index < buffer.Length) + { + Paragraph p = ReadParagraph(buffer, index); + if (p != null) + subtitle.Paragraphs.Add(p); + index += 128; + } + } + + private Queue _timeCodeQueue = new Queue(); + + private Paragraph ReadParagraph(byte[] buffer, int index) + { + if (buffer[index] == 1 && _timeCodeQueue.Count > 0) // text + { + var sb = new StringBuilder(); + int skipCount = 0; + int textLength = buffer[index + 2] - 11; + int start = index + 13; + + for (int i = 0; i <= textLength; i++) + { + if (skipCount > 0) + { + skipCount--; + } + else if (buffer[index + 13 + i] == 0xFE) + { + sb.Append(GetText(buffer, start, index + i + 13)); + start = index + 13 + i + 3; + skipCount = 2; + sb.AppendLine(); + } + else if (buffer[index + 13 + i] == 0) + { + sb.Append(GetText(buffer, start, index + i + 13)); + break; + } + if (i == textLength) + { + sb.Append(GetText(buffer, start, index + i + 13 + 1)); + } + } + + Paragraph p; + if (_timeCodeQueue.Count > 0) + p = _timeCodeQueue.Dequeue(); + else + p = new Paragraph(); + p.Number = buffer[index + 3] * 256 + buffer[index + 4]; // Subtitle number + p.Text = sb.ToString(); + //if (p.Number == 0 && p.Text.StartsWith("LANG:", StringComparison.Ordinal) && p.Text.Length > 8) + //{ + // _languageId = p.Text.Substring(5, 3); + //} + return p; + } + if (buffer[index] == 0x0a && _timeCodeQueue.Count > 0) + { + // ? + } + else if (buffer[index] == 0x09 && _timeCodeQueue.Count > 0) + { + // ? + } + else // time codes + { + _timeCodeQueue = new Queue(); + for (int i = 0; i < 15; i++) + { + int start = index + 2 + (i * 8); + int totalFrameNumber = (buffer[start + 3] << 16) + (buffer[start + 5] << 8) + buffer[start + 4]; + int durationInFrames = buffer[start + 6]; + var p = new Paragraph(string.Empty, FramesToMilliseconds(totalFrameNumber), FramesToMilliseconds(totalFrameNumber + durationInFrames)); + _timeCodeQueue.Enqueue(p); + } + } + return null; + } + + private string GetText(byte[] buffer, int start, int end) + { + string text = string.Empty; + if (buffer[start] == 0x1f && buffer[start + 1] == 0x57 && buffer[start + 2] == 0x31 && buffer[start + 3] == 0x36) // W16 + { + if (end - start > 4) + text = Encoding.GetEncoding(950).GetString(buffer, start + 4, end - start - 4); + } + else + { + if (end - start > 0) + text = _codePage.GetString(buffer, start, end - start); + } + if (text.Length > 4 && text[0] == 0x1f && text[1] == 'R' && text[4] == '.' && "0123456789".Contains(text[2]) && "0123456789".Contains(text[3])) + { + text = text.Remove(0, 5); + } + + + // special language codes... + text = text.Replace("ÔA", "Á"); + text = text.Replace("ÔE", "É"); + text = text.Replace("ÔI", "Í"); + text = text.Replace("ÓN", "Ñ"); + text = text.Replace("ÔO", "Ó"); + text = text.Replace("ÔU", "Ú"); + text = text.Replace("Ôa", "á"); + text = text.Replace("Ôe", "é"); + text = text.Replace("Ôi", "í"); + text = text.Replace("Ón", "ñ"); + text = text.Replace("Ôo", "ó"); + text = text.Replace("Ôu", "ú"); + + text = text.Replace("ÒA", "À"); + text = text.Replace("ÒE", "È"); + text = text.Replace("ÒU", "Ù"); + text = text.Replace("Òa", "à"); + text = text.Replace("Òe", "è"); + text = text.Replace("Òu", "ù"); + + text = text.Replace("ÕU", "Ü"); + text = text.Replace("ÕA", "Ä"); + text = text.Replace("ÕO", "Ö"); + text = text.Replace("Õu", "ü"); + text = text.Replace("Õa", "ä"); + text = text.Replace("Õo", "ö"); + + text = text.Replace("õa", "â"); + text = text.Replace("õe", "ê"); + text = text.Replace("õi", "î"); + text = text.Replace("õu", "û"); + text = text.Replace("õA", "Â"); + text = text.Replace("õE", "Ê"); + text = text.Replace("õI", "Î"); + text = text.Replace("õU", "Û"); + + return ApplyFont(text); + } + + private static string ApplyFont(string text) + { + var sb = new StringBuilder(); + string post = string.Empty; + int i = 0; + while (i < text.Length) + { + if (text[i] == 01 && i < text.Length - 4 && text[i + 1] == 0x1D && text[i + 2] == 07) + { + if (post != string.Empty) + sb.Append("
"); + sb.Append(""); + post = ""; + i += 2; + } + else if (text[i] == 01 && i < text.Length - 4 && text[i + 1] == 06) + { + if (post != string.Empty) + sb.Append(""); + sb.Append(""); + post = ""; + i++; + } + else if (text[i] == 2) + { + if (post != string.Empty) + sb.Append(""); + sb.Append(""); + post = ""; + } + else if (text[i] == 3) + { + if (post != string.Empty) + sb.Append(""); + sb.Append(""); + post = ""; + } + else if (text[i] == 6) + { + if (post != string.Empty) + sb.Append(""); + sb.Append(""); + post = ""; + } + else if (text[i] == 7) + { + if (post != string.Empty) + sb.Append(""); + sb.Append(""); + post = ""; + } + else + { + sb.Append(text[i]); + } + i++; + } + + text = sb + post; + if (string.IsNullOrWhiteSpace(HtmlUtil.RemoveHtmlTags(text))) + return string.Empty; + return text; + } + + } +} diff --git a/libse/SubtitleFormats/Csv.cs b/libse/SubtitleFormats/Csv.cs new file mode 100644 index 000000000..1388899b9 --- /dev/null +++ b/libse/SubtitleFormats/Csv.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class Csv : SubtitleFormat + { + private const string Separator = ";"; + private static readonly Regex CsvLine = new Regex(@"^""?\d+""?" + Separator + @"""?\d+""?" + Separator + @"""?\d+""?" + Separator + @"""?[^""]*""?$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".csv"; } + } + + public override string Name + { + get { return "Csv"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + int fine = 0; + int failed = 0; + foreach (string line in lines) + { + if (CsvLine.IsMatch(line)) + fine++; + else + failed++; + } + return fine > failed; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string format = "{1}{0}{2}{0}{3}{0}\"{4}\""; + var sb = new StringBuilder(); + sb.AppendLine(string.Format(format, Separator, "Number", "Start time in milliseconds", "End time in milliseconds", "Text")); + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.AppendLine(string.Format(format, Separator, p.Number, p.StartTime.TotalMilliseconds, p.EndTime.TotalMilliseconds, p.Text.Replace(Environment.NewLine, "\n"))); + } + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + bool continuation = false; + Paragraph p = null; + foreach (string line in lines) + { + if (CsvLine.IsMatch(line)) + { + string[] parts = line.Split(Separator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 4) + try + { + int start = Convert.ToInt32(Utilities.FixQuotes(parts[1])); + int end = Convert.ToInt32(Utilities.FixQuotes(parts[2])); + string text = Utilities.FixQuotes(parts[3]); + p = new Paragraph(text, start, end); + subtitle.Paragraphs.Add(p); + continuation = parts[3].StartsWith('"') && !parts[3].EndsWith('"'); + } + catch + { + _errorCount++; + } + } + else + { + if (continuation) + { + if (p.Text.Length < 300) + p.Text = (p.Text + Environment.NewLine + line.TrimEnd('"')).Trim(); + continuation = !line.TrimEnd().EndsWith('"'); + } + else + { + _errorCount++; + } + } + } + subtitle.Renumber(); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/Csv2.cs b/libse/SubtitleFormats/Csv2.cs new file mode 100644 index 000000000..ca76f7a60 --- /dev/null +++ b/libse/SubtitleFormats/Csv2.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class Csv2 : SubtitleFormat + { + + private const string Separator = ","; + + //1,01:00:10:03,01:00:15:25,I thought I should let my sister-in-law know. + private static readonly Regex CsvLine = new Regex(@"^\d+" + Separator + @"\d\d:\d\d:\d\d:\d\d" + Separator + @"\d\d:\d\d:\d\d:\d\d" + Separator, RegexOptions.Compiled); + + public override string Extension + { + get { return ".csv"; } + } + + public override string Name + { + get { return "Csv2"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + int fine = 0; + int failed = 0; + bool continuation = false; + foreach (string line in lines) + { + Match m = CsvLine.Match(line); + if (m.Success) + { + fine++; + string s = line.Remove(0, m.Length); + continuation = s.StartsWith('"'); + } + else if (!string.IsNullOrWhiteSpace(line)) + { + if (continuation) + continuation = false; + else + failed++; + } + } + return fine > failed; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string format = "{1}{0}{2}{0}{3}{0}\"{4}\""; + var sb = new StringBuilder(); + sb.AppendLine(string.Format(format, Separator, "Number", "Start time (hh:mm:ss:ff)", "End time (hh:mm:ss:ff)", "Text")); + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.AppendLine(string.Format(format, Separator, p.Number, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), p.Text.Replace(Environment.NewLine, "\n"))); + } + return sb.ToString().Trim(); + } + + private static string EncodeTimeCode(TimeCode time) + { + return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", time.Hours, time.Minutes, time.Seconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + bool continuation = false; + Paragraph p = null; + foreach (string line in lines) + { + Match m = CsvLine.Match(line); + if (m.Success) + { + string[] parts = line.Substring(0, m.Length).Split(Separator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 3) + try + { + var start = DecodeTimeCode(parts[1]); + var end = DecodeTimeCode(parts[2]); + string text = line.Remove(0, m.Length); + continuation = text.StartsWith('"') && !text.EndsWith('"'); + text = text.Trim('"'); + p = new Paragraph(start, end, text); + subtitle.Paragraphs.Add(p); + } + catch + { + _errorCount++; + } + } + else if (!string.IsNullOrWhiteSpace(line)) + { + if (continuation) + { + if (p != null && p.Text.Length < 300) + p.Text = (p.Text + Environment.NewLine + line.TrimEnd('"')).Trim(); + continuation = !line.TrimEnd().EndsWith('"'); + } + else + { + _errorCount++; + } + } + } + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string part) + { + string[] parts = part.Split(new[] { '.', ':' }, StringSplitOptions.RemoveEmptyEntries); + + //00:00:07:12 + string hour = parts[0]; + string minutes = parts[1]; + string seconds = parts[2]; + string frames = parts[3]; + + return new TimeCode(int.Parse(hour), int.Parse(minutes), int.Parse(seconds), FramesToMillisecondsMax999(int.Parse(frames))); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/Csv3.cs b/libse/SubtitleFormats/Csv3.cs new file mode 100644 index 000000000..64a2bf8f0 --- /dev/null +++ b/libse/SubtitleFormats/Csv3.cs @@ -0,0 +1,194 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class Csv3 : SubtitleFormat + { + + private const string Separator = ","; + + //01:00:10:03,01:00:15:25,"I thought I should let my sister-in-law know.", "" + private static readonly Regex CsvLine = new Regex(@"^\d\d:\d\d:\d\d:\d\d" + Separator + @"\d\d:\d\d:\d\d:\d\d" + Separator, RegexOptions.Compiled); + + public override string Extension + { + get { return ".csv"; } + } + + public override string Name + { + get { return "Csv3"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + int fine = 0; + int failed = 0; + bool continuation = false; + foreach (string line in lines) + { + if (line.StartsWith("$FontName", StringComparison.Ordinal) || line.StartsWith("$ColorIndex1", StringComparison.Ordinal)) + return false; + Match m = null; + if (line.Length > 8 && line[2] == ':') + m = CsvLine.Match(line); + if (m != null && m.Success) + { + fine++; + string s = line.Remove(0, m.Length); + continuation = s.StartsWith('"'); + } + else if (!string.IsNullOrWhiteSpace(line)) + { + if (continuation) + continuation = false; + else + failed++; + } + } + if (failed > 20) + return false; + + return fine > failed; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string format = "{1}{0}{2}{0}\"{3}\"{0}\"{4}\""; + var sb = new StringBuilder(); + sb.AppendLine(string.Format(format, Separator, "Start time (hh:mm:ss:ff)", "End time (hh:mm:ss:ff)", "Line 1", "Line 2")); + foreach (Paragraph p in subtitle.Paragraphs) + { + var arr = p.Text.Trim().SplitToLines(); + if (arr.Length > 3) + { + string s = Utilities.AutoBreakLine(p.Text); + arr = s.Trim().SplitToLines(); + } + string line1 = string.Empty; + string line2 = string.Empty; + if (arr.Length > 0) + line1 = arr[0]; + if (arr.Length > 1) + line2 = arr[1]; + line1 = line1.Replace("\"", "\"\""); + line2 = line2.Replace("\"", "\"\""); + sb.AppendLine(string.Format(format, Separator, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), line1, line2)); + } + return sb.ToString().Trim(); + } + + private static string EncodeTimeCode(TimeCode time) + { + return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", time.Hours, time.Minutes, time.Seconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + foreach (string line in lines) + { + Match m = CsvLine.Match(line); + if (m.Success) + { + string[] parts = line.Substring(0, m.Length).Split(Separator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 2) + try + { + var start = DecodeTimeCode(parts[0]); + var end = DecodeTimeCode(parts[1]); + string text = ReadText(line.Remove(0, m.Length)); + var p = new Paragraph(start, end, text); + subtitle.Paragraphs.Add(p); + } + catch + { + _errorCount++; + } + } + else if (!string.IsNullOrWhiteSpace(line)) + { + _errorCount++; + } + } + subtitle.Renumber(); + } + + private static string ReadText(string csv) + { + if (string.IsNullOrEmpty(csv)) + return string.Empty; + + csv = csv.Replace("\"\"", "\""); + + var sb = new StringBuilder(); + csv = csv.Trim(); + if (csv.StartsWith('"')) + csv = csv.Remove(0, 1); + if (csv.EndsWith('"')) + csv = csv.Remove(csv.Length - 1, 1); + bool isBreak = false; + for (int i = 0; i < csv.Length; i++) + { + var s = csv[i]; + if (s == '"' && csv.Substring(i).StartsWith("\"\"")) + { + sb.Append('"'); + } + else if (s == '"') + { + if (isBreak) + { + isBreak = false; + } + else if (i == 0 || i == csv.Length - 1 || sb.ToString().EndsWith(Environment.NewLine)) + { + sb.Append('"'); + } + else + { + isBreak = true; + } + } + else + { + if (isBreak && s == ' ') + { + } + else if (isBreak && s == ',') + { + sb.Append(Environment.NewLine); + } + else + { + isBreak = false; + sb.Append(s); + } + } + } + return sb.ToString().Trim(); + } + + private static TimeCode DecodeTimeCode(string part) + { + string[] parts = part.Split(new[] { '.', ':' }, StringSplitOptions.RemoveEmptyEntries); + + //00:00:07:12 + string hour = parts[0]; + string minutes = parts[1]; + string seconds = parts[2]; + string frames = parts[3]; + + return new TimeCode(int.Parse(hour), int.Parse(minutes), int.Parse(seconds), FramesToMillisecondsMax999(int.Parse(frames))); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/DCSubtitle.cs b/libse/SubtitleFormats/DCSubtitle.cs new file mode 100644 index 000000000..32153987b --- /dev/null +++ b/libse/SubtitleFormats/DCSubtitle.cs @@ -0,0 +1,737 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + + // https://github.com/SubtitleEdit/subtitleedit/issues/detail?id=18 + // + // + // 4EB245B8-4D3A-4158-9516-95DD20E8322E + // Unknown + // 1 + // Swedish + // + // + // DETTA HAR HÄNT... + // + // + // + + public class DCSubtitle : SubtitleFormat + { + public override string Extension + { + get { return ".xml"; } + } + + public override string Name + { + get { return "D-Cinema interop"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + string xmlAsString = sb.ToString().Trim(); + if (xmlAsString.Contains(" 0; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + return false; + } + } + else + { + return false; + } + } + + private static string RemoveSubStationAlphaFormatting(string s) + { + int indexOfBegin = s.IndexOf('{'); + while (indexOfBegin >= 0 && s.IndexOf('}') > indexOfBegin) + { + int indexOfEnd = s.IndexOf('}'); + s = s.Remove(indexOfBegin, (indexOfEnd - indexOfBegin) + 1); + indexOfBegin = s.IndexOf('{'); + } + return s; + } + + public override string ToText(Subtitle subtitle, string title) + { + string languageEnglishName; + try + { + string languageShortName = Utilities.AutoDetectGoogleLanguage(subtitle); + var ci = CultureInfo.CreateSpecificCulture(languageShortName); + languageEnglishName = ci.EnglishName; + int indexOfStartP = languageEnglishName.IndexOf('('); + if (indexOfStartP > 1) + languageEnglishName = languageEnglishName.Remove(indexOfStartP).Trim(); + } + catch + { + languageEnglishName = "English"; + } + + string hex = Guid.NewGuid().ToString().Replace("-", string.Empty); + hex = hex.Insert(8, "-").Insert(13, "-").Insert(18, "-").Insert(23, "-"); + + string xmlStructure = "" + Environment.NewLine + + " " + hex.ToLower() + "" + Environment.NewLine + + " " + Environment.NewLine + + " 1" + Environment.NewLine + + " " + languageEnglishName + "" + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + ""; + + XmlDocument xml = new XmlDocument(); + xml.LoadXml(xmlStructure); + xml.PreserveWhitespace = true; + + var ss = Configuration.Settings.SubtitleSettings; + string loadedFontId = "Font1"; + if (!string.IsNullOrEmpty(ss.CurrentDCinemaFontId)) + loadedFontId = ss.CurrentDCinemaFontId; + + if (string.IsNullOrEmpty(ss.CurrentDCinemaMovieTitle)) + ss.CurrentDCinemaMovieTitle = title; + + if (ss.CurrentDCinemaFontSize == 0 || string.IsNullOrEmpty(ss.CurrentDCinemaFontEffect)) + Configuration.Settings.SubtitleSettings.InitializeDCinameSettings(true); + + xml.DocumentElement.SelectSingleNode("MovieTitle").InnerText = ss.CurrentDCinemaMovieTitle; + xml.DocumentElement.SelectSingleNode("SubtitleID").InnerText = ss.CurrentDCinemaSubtitleId.Replace("urn:uuid:", string.Empty); + xml.DocumentElement.SelectSingleNode("ReelNumber").InnerText = ss.CurrentDCinemaReelNumber; + xml.DocumentElement.SelectSingleNode("Language").InnerText = ss.CurrentDCinemaLanguage; + xml.DocumentElement.SelectSingleNode("LoadFont").Attributes["URI"].InnerText = ss.CurrentDCinemaFontUri; + xml.DocumentElement.SelectSingleNode("LoadFont").Attributes["Id"].InnerText = loadedFontId; + int fontSize = ss.CurrentDCinemaFontSize; + xml.DocumentElement.SelectSingleNode("Font").Attributes["Id"].InnerText = loadedFontId; + xml.DocumentElement.SelectSingleNode("Font").Attributes["Color"].InnerText = "FF" + Utilities.ColorToHex(ss.CurrentDCinemaFontColor).TrimStart('#').ToUpper(); + xml.DocumentElement.SelectSingleNode("Font").Attributes["Effect"].InnerText = ss.CurrentDCinemaFontEffect; + xml.DocumentElement.SelectSingleNode("Font").Attributes["EffectColor"].InnerText = "FF" + Utilities.ColorToHex(ss.CurrentDCinemaFontEffectColor).TrimStart('#').ToUpper(); + xml.DocumentElement.SelectSingleNode("Font").Attributes["Size"].InnerText = ss.CurrentDCinemaFontSize.ToString(); + + XmlNode mainListFont = xml.DocumentElement.SelectSingleNode("Font"); + int no = 0; + foreach (Paragraph p in subtitle.Paragraphs) + { + if (!string.IsNullOrEmpty(p.Text)) + { + XmlNode subNode = xml.CreateElement("Subtitle"); + + XmlAttribute id = xml.CreateAttribute("SpotNumber"); + id.InnerText = (no + 1).ToString(); + subNode.Attributes.Append(id); + + XmlAttribute fadeUpTime = xml.CreateAttribute("FadeUpTime"); + fadeUpTime.InnerText = Configuration.Settings.SubtitleSettings.DCinemaFadeUpTime.ToString(); + subNode.Attributes.Append(fadeUpTime); + + XmlAttribute fadeDownTime = xml.CreateAttribute("FadeDownTime"); + fadeDownTime.InnerText = Configuration.Settings.SubtitleSettings.DCinemaFadeDownTime.ToString(); + subNode.Attributes.Append(fadeDownTime); + + XmlAttribute start = xml.CreateAttribute("TimeIn"); + start.InnerText = ConvertToTimeString(p.StartTime); + subNode.Attributes.Append(start); + + XmlAttribute end = xml.CreateAttribute("TimeOut"); + end.InnerText = ConvertToTimeString(p.EndTime); + subNode.Attributes.Append(end); + + bool alignLeft = p.Text.StartsWith("{\\a1}") || p.Text.StartsWith("{\\a5}") || p.Text.StartsWith("{\\a9}") || // sub station alpha + p.Text.StartsWith("{\\an1}") || p.Text.StartsWith("{\\an4}") || p.Text.StartsWith("{\\an7}"); // advanced sub station alpha + + bool alignRight = p.Text.StartsWith("{\\a3}") || p.Text.StartsWith("{\\a7}") || p.Text.StartsWith("{\\a11}") || // sub station alpha + p.Text.StartsWith("{\\an3}") || p.Text.StartsWith("{\\an6}") || p.Text.StartsWith("{\\an9}"); // advanced sub station alpha + + bool alignVTop = p.Text.StartsWith("{\\a5}") || p.Text.StartsWith("{\\a6}") || p.Text.StartsWith("{\\a7}") || // sub station alpha + p.Text.StartsWith("{\\an7}") || p.Text.StartsWith("{\\an8}") || p.Text.StartsWith("{\\an9}"); // advanced sub station alpha + + bool alignVCenter = p.Text.StartsWith("{\\a9}") || p.Text.StartsWith("{\\a10}") || p.Text.StartsWith("{\\a11}") || // sub station alpha + p.Text.StartsWith("{\\an4}") || p.Text.StartsWith("{\\an5}") || p.Text.StartsWith("{\\an6}"); // advanced sub station alpha + + // remove styles for display text (except italic) + string text = RemoveSubStationAlphaFormatting(p.Text); + + var lines = text.SplitToLines(); + int vPos = 1 + lines.Length * 7; + int vPosFactor = (int)Math.Round(fontSize / 7.4); + if (alignVTop) + { + vPos = Configuration.Settings.SubtitleSettings.DCinemaBottomMargin; // Bottom margin is normally 8 + } + else if (alignVCenter) + { + vPos = (int)Math.Round((lines.Length * vPosFactor * -1) / 2.0); + } + else + { + vPos = (lines.Length * vPosFactor) - vPosFactor + Configuration.Settings.SubtitleSettings.DCinemaBottomMargin; // Bottom margin is normally 8 + } + + bool isItalic = false; + int fontNo = 0; + Stack fontColors = new Stack(); + foreach (string line in lines) + { + XmlNode textNode = xml.CreateElement("Text"); + + XmlAttribute vPosition = xml.CreateAttribute("VPosition"); + vPosition.InnerText = vPos.ToString(); + textNode.Attributes.Append(vPosition); + + if (Configuration.Settings.SubtitleSettings.DCinemaZPosition != 0) + { + XmlAttribute zPosition = xml.CreateAttribute("ZPosition"); + zPosition.InnerText = string.Format(CultureInfo.InvariantCulture, "{0:0.00}", Configuration.Settings.SubtitleSettings.DCinemaZPosition); + textNode.Attributes.Append(zPosition); + } + + XmlAttribute vAlign = xml.CreateAttribute("VAlign"); + if (alignVTop) + vAlign.InnerText = "top"; + else if (alignVCenter) + vAlign.InnerText = "center"; + else + vAlign.InnerText = "bottom"; + textNode.Attributes.Append(vAlign); + + XmlAttribute hAlign = xml.CreateAttribute("HAlign"); + if (alignLeft) + hAlign.InnerText = "left"; + else if (alignRight) + hAlign.InnerText = "right"; + else + hAlign.InnerText = "center"; + textNode.Attributes.Append(hAlign); + + XmlAttribute direction = xml.CreateAttribute("Direction"); + direction.InnerText = "horizontal"; + textNode.Attributes.Append(direction); + + int i = 0; + var txt = new StringBuilder(); + var html = new StringBuilder(); + XmlNode nodeTemp = xml.CreateElement("temp"); + while (i < line.Length) + { + if (!isItalic && line.Substring(i).StartsWith("")) + { + if (txt.Length > 0) + { + nodeTemp.InnerText = txt.ToString(); + html.Append(nodeTemp.InnerXml); + txt = new StringBuilder(); + } + isItalic = true; + i += 2; + } + else if (isItalic && line.Substring(i).StartsWith("")) + { + if (txt.Length > 0) + { + XmlNode fontNode = xml.CreateElement("Font"); + + XmlAttribute italic = xml.CreateAttribute("Italic"); + italic.InnerText = "yes"; + fontNode.Attributes.Append(italic); + + if (!string.IsNullOrEmpty(ss.CurrentDCinemaFontEffect)) + { + XmlAttribute fontEffect = xml.CreateAttribute("Effect"); + fontEffect.InnerText = ss.CurrentDCinemaFontEffect; + fontNode.Attributes.Append(fontEffect); + } + + if (line.Length > i + 5 && line.Substring(i + 4).StartsWith("")) + { + XmlAttribute fontColor = xml.CreateAttribute("Color"); + fontColor.InnerText = fontColors.Pop(); + fontNode.Attributes.Append(fontColor); + fontNo--; + i += 7; + } + + fontNode.InnerText = HtmlUtil.RemoveHtmlTags(txt.ToString()); + html.Append(fontNode.OuterXml); + txt = new StringBuilder(); + } + isItalic = false; + i += 3; + } + else if (line.Substring(i).StartsWith(" 0 && line.Substring(i).StartsWith("")) + { + if (txt.Length > 0) + { + XmlNode fontNode = xml.CreateElement("Font"); + + XmlAttribute fontColor = xml.CreateAttribute("Color"); + fontColor.InnerText = fontColors.Pop(); + fontNode.Attributes.Append(fontColor); + + if (line.Length > i + 9 && line.Substring(i + 7).StartsWith("")) + { + XmlAttribute italic = xml.CreateAttribute("Italic"); + italic.InnerText = "yes"; + fontNode.Attributes.Append(italic); + isItalic = false; + i += 4; + } + + if (!string.IsNullOrEmpty(ss.CurrentDCinemaFontEffect)) + { + XmlAttribute fontEffect = xml.CreateAttribute("Effect"); + fontEffect.InnerText = ss.CurrentDCinemaFontEffect; + fontNode.Attributes.Append(fontEffect); + } + + fontNode.InnerText = HtmlUtil.RemoveHtmlTags(txt.ToString()); + html.Append(fontNode.OuterXml); + txt = new StringBuilder(); + } + fontNo--; + i += 6; + } + else + { + txt.Append(line[i]); + } + i++; + } + if (fontNo > 0) + { + if (txt.Length > 0) + { + XmlNode fontNode = xml.CreateElement("Font"); + + XmlAttribute fontColor = xml.CreateAttribute("Color"); + fontColor.InnerText = fontColors.Peek(); + fontNode.Attributes.Append(fontColor); + + if (isItalic) + { + XmlAttribute italic = xml.CreateAttribute("Italic"); + italic.InnerText = "yes"; + fontNode.Attributes.Append(italic); + } + + if (!string.IsNullOrEmpty(ss.CurrentDCinemaFontEffect)) + { + XmlAttribute fontEffect = xml.CreateAttribute("Effect"); + fontEffect.InnerText = ss.CurrentDCinemaFontEffect; + fontNode.Attributes.Append(fontEffect); + } + + fontNode.InnerText = HtmlUtil.RemoveHtmlTags(txt.ToString()); + html.Append(fontNode.OuterXml); + } + else if (html.Length > 0 && html.ToString().StartsWith("" + html + ""); + XmlNode fontNode = xml.CreateElement("Font"); + fontNode.InnerXml = temp.DocumentElement.SelectSingleNode("Font").InnerXml; + foreach (XmlAttribute a in temp.DocumentElement.SelectSingleNode("Font").Attributes) + { + XmlAttribute newA = xml.CreateAttribute(a.Name); + newA.InnerText = a.InnerText; + fontNode.Attributes.Append(newA); + } + + XmlAttribute fontColor = xml.CreateAttribute("Color"); + fontColor.InnerText = fontColors.Peek(); + fontNode.Attributes.Append(fontColor); + + if (!string.IsNullOrEmpty(ss.CurrentDCinemaFontEffect)) + { + XmlAttribute fontEffect = xml.CreateAttribute("Effect"); + fontEffect.InnerText = ss.CurrentDCinemaFontEffect; + fontNode.Attributes.Append(fontEffect); + } + + html = new StringBuilder(); + html.Append(fontNode.OuterXml); + } + } + else if (isItalic) + { + if (txt.Length > 0) + { + XmlNode fontNode = xml.CreateElement("Font"); + + XmlAttribute italic = xml.CreateAttribute("Italic"); + italic.InnerText = "yes"; + fontNode.Attributes.Append(italic); + + if (!string.IsNullOrEmpty(ss.CurrentDCinemaFontEffect)) + { + XmlAttribute fontEffect = xml.CreateAttribute("Effect"); + fontEffect.InnerText = ss.CurrentDCinemaFontEffect; + fontNode.Attributes.Append(fontEffect); + } + + fontNode.InnerText = HtmlUtil.RemoveHtmlTags(line); + html.Append(fontNode.OuterXml); + } + } + else + { + if (txt.Length > 0) + { + nodeTemp.InnerText = txt.ToString(); + html.Append(nodeTemp.InnerXml); + } + } + textNode.InnerXml = html.ToString(); + + subNode.AppendChild(textNode); + if (alignVTop) + vPos += vPosFactor; + else + vPos -= vPosFactor; + } + + mainListFont.AppendChild(subNode); + no++; + } + } + string s = ToUtf8XmlString(xml).Replace("encoding=\"utf-8\"", "encoding=\"UTF-8\""); + while (s.Contains(" ") || s.Contains(" " + Environment.NewLine)) + { + while (s.Contains(" Font")) + s = s.Replace(" ", " "); + s = s.Replace(" " + Environment.NewLine, ""); + s = s.Replace("><", "> <"); + } + return s; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + var xml = new XmlDocument(); + xml.XmlResolver = null; + xml.LoadXml(sb.ToString().Trim()); + + var ss = Configuration.Settings.SubtitleSettings; + try + { + ss.InitializeDCinameSettings(false); + XmlNode node = xml.DocumentElement.SelectSingleNode("SubtitleID"); + if (node != null) + ss.CurrentDCinemaSubtitleId = node.InnerText; + + node = xml.DocumentElement.SelectSingleNode("ReelNumber"); + if (node != null) + ss.CurrentDCinemaReelNumber = node.InnerText; + + node = xml.DocumentElement.SelectSingleNode("Language"); + if (node != null) + ss.CurrentDCinemaLanguage = node.InnerText; + + node = xml.DocumentElement.SelectSingleNode("MovieTitle"); + if (node != null) + ss.CurrentDCinemaMovieTitle = node.InnerText; + + node = xml.DocumentElement.SelectSingleNode("LoadFont"); + if (node != null) + { + if (node.Attributes["URI"] != null) + ss.CurrentDCinemaFontUri = node.Attributes["URI"].InnerText; + } + + node = xml.DocumentElement.SelectSingleNode("Font"); + if (node != null) + { + if (node.Attributes["ID"] != null) + ss.CurrentDCinemaFontId = node.Attributes["ID"].InnerText; + if (node.Attributes["Size"] != null) + ss.CurrentDCinemaFontSize = Convert.ToInt32(node.Attributes["Size"].InnerText); + if (node.Attributes["Color"] != null) + ss.CurrentDCinemaFontColor = System.Drawing.ColorTranslator.FromHtml("#" + node.Attributes["Color"].InnerText); + if (node.Attributes["Effect"] != null) + ss.CurrentDCinemaFontEffect = node.Attributes["Effect"].InnerText; + if (node.Attributes["EffectColor"] != null) + ss.CurrentDCinemaFontEffectColor = System.Drawing.ColorTranslator.FromHtml("#" + node.Attributes["EffectColor"].InnerText); + } + } + catch (Exception exception) + { + System.Diagnostics.Debug.WriteLine(exception.Message); + } + + foreach (XmlNode node in xml.DocumentElement.SelectNodes("//Subtitle")) + { + try + { + var pText = new StringBuilder(); + string lastVPosition = string.Empty; + foreach (XmlNode innerNode in node.ChildNodes) + { + switch (innerNode.Name) + { + case "Text": + if (innerNode.Attributes["VPosition"] != null) + { + string vPosition = innerNode.Attributes["VPosition"].InnerText; + if (vPosition != lastVPosition) + { + if (pText.Length > 0 && lastVPosition.Length > 0) + pText.AppendLine(); + lastVPosition = vPosition; + } + } + bool alignLeft = false; + bool alignRight = false; + bool alignVTop = false; + bool alignVCenter = false; + if (innerNode.Attributes["HAlign"] != null) + { + string hAlign = innerNode.Attributes["HAlign"].InnerText; + if (hAlign == "left") + alignLeft = true; + else if (hAlign == "right") + alignRight = true; + } + if (innerNode.Attributes["VAlign"] != null) + { + string hAlign = innerNode.Attributes["VAlign"].InnerText; + if (hAlign == "top") + alignVTop = true; + else if (hAlign == "center") + alignVCenter = true; + } + if (alignLeft || alignRight || alignVCenter || alignVTop) + { + if (!pText.ToString().StartsWith("{\\an")) + { + string pre = string.Empty; + if (alignVTop) + { + if (alignLeft) + pre = "{\\an7}"; + else if (alignRight) + pre = "{\\an9}"; + else + pre = "{\\an8}"; + } + else if (alignVCenter) + { + if (alignLeft) + pre = "{\\an4}"; + else if (alignRight) + pre = "{\\an6}"; + else + pre = "{\\an5}"; + } + else + { + if (alignLeft) + pre = "{\\an1}"; + else if (alignRight) + pre = "{\\an3}"; + } + string temp = pre + pText; + pText = new StringBuilder(); + pText.Append(temp); + } + } + + if (innerNode.ChildNodes.Count == 0) + { + pText.Append(innerNode.InnerText); + } + else + { + foreach (XmlNode innerInnerNode in innerNode) + { + if (innerInnerNode.Name == "Font" && innerInnerNode.Attributes["Italic"] != null && + innerInnerNode.Attributes["Italic"].InnerText.Equals("yes", StringComparison.OrdinalIgnoreCase)) + { + if (innerInnerNode.Attributes["Color"] != null) + pText.Append("" + innerInnerNode.InnerText + ""); + else + pText.Append("" + innerInnerNode.InnerText + ""); + } + else if (innerInnerNode.Name == "Font" && innerInnerNode.Attributes["Color"] != null) + { + if (innerInnerNode.Attributes["Italic"] != null && innerInnerNode.Attributes["Italic"].InnerText.Equals("yes", StringComparison.OrdinalIgnoreCase)) + pText.Append("" + innerInnerNode.InnerText + ""); + else + pText.Append("" + innerInnerNode.InnerText + ""); + } + else + { + pText.Append(innerInnerNode.InnerText); + } + } + } + break; + default: + pText.Append(innerNode.InnerText); + break; + } + } + string start = node.Attributes["TimeIn"].InnerText; + string end = node.Attributes["TimeOut"].InnerText; + + if (node.ParentNode.Name == "Font" && node.ParentNode.Attributes["Italic"] != null && node.ParentNode.Attributes["Italic"].InnerText.Equals("yes", StringComparison.OrdinalIgnoreCase) && + !pText.ToString().Contains("")) + { + string text = pText.ToString(); + if (text.StartsWith("{\\an") && text.Length > 6) + text = text.Insert(6, "") + ""; + else + text = "" + text + ""; + pText = new StringBuilder(text); + } + + subtitle.Paragraphs.Add(new Paragraph(GetTimeCode(start), GetTimeCode(end), pText.ToString())); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + _errorCount++; + } + } + + if (subtitle.Paragraphs.Count > 0) + subtitle.Header = xml.OuterXml; // save id/language/font for later use + + subtitle.Renumber(); + } + + private static string GetColorStringForDCinema(string p) + { + string s = p.ToUpper().Trim(); + if (s.Replace("#", string.Empty). + Replace("0", string.Empty). + Replace("1", string.Empty). + Replace("2", string.Empty). + Replace("3", string.Empty). + Replace("4", string.Empty). + Replace("5", string.Empty). + Replace("6", string.Empty). + Replace("7", string.Empty). + Replace("8", string.Empty). + Replace("9", string.Empty). + Replace("A", string.Empty). + Replace("B", string.Empty). + Replace("C", string.Empty). + Replace("D", string.Empty). + Replace("E", string.Empty). + Replace("F", string.Empty).Length == 0) + { + return s.TrimStart('#'); + } + else + { + return p; + } + } + + private static string GetColorStringFromDCinema(string p) + { + string s = p.ToLower().Trim(); + if (s.Replace("#", string.Empty). + Replace("0", string.Empty). + Replace("1", string.Empty). + Replace("2", string.Empty). + Replace("3", string.Empty). + Replace("4", string.Empty). + Replace("5", string.Empty). + Replace("6", string.Empty). + Replace("7", string.Empty). + Replace("8", string.Empty). + Replace("9", string.Empty). + Replace("a", string.Empty). + Replace("b", string.Empty). + Replace("c", string.Empty). + Replace("d", string.Empty). + Replace("e", string.Empty). + Replace("f", string.Empty).Length == 0) + { + if (s.StartsWith('#')) + return s; + else + return "#" + s; + } + else + { + return p; + } + } + + private static TimeCode GetTimeCode(string s) + { + string[] parts = s.Split(new char[] { ':', '.', ',' }); + + int milliseconds = (int)(int.Parse(parts[3]) * 4); // 000 to 249 + if (s.Contains('.')) + milliseconds = int.Parse(parts[3].PadRight(3, '0')); + if (milliseconds > 999) + milliseconds = 999; + + return new TimeCode(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), milliseconds); + } + + public static string ConvertToTimeString(TimeCode time) + { + return string.Format("{0:00}:{1:00}:{2:00}:{3:000}", time.Hours, time.Minutes, time.Seconds, time.Milliseconds / 4); + } + + } +} diff --git a/libse/SubtitleFormats/DCinemaSmpte2007.cs b/libse/SubtitleFormats/DCinemaSmpte2007.cs new file mode 100644 index 000000000..dc26ee5d7 --- /dev/null +++ b/libse/SubtitleFormats/DCinemaSmpte2007.cs @@ -0,0 +1,779 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Text; +using System.Xml; +using System.Xml.Schema; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class DCinemaSmpte2007 : SubtitleFormat + { + // + // + // urn:uuid:7be835a3-cfb4-43d0-bb4b-f0b4c95e962e + // 2001, A Space Odissey + // This is a subtitle file + // 2012-06-26T12:33:59.000-00:00 + // 1 + // fr + // 25 1 + // 25 + // 00:00:00:00 + // urn:uuid:3dec6dc0-39d0-498d-97d0-928d2eb78391 + // + // + // Hallo + // + // + + private double _frameRate = 24; + + public int Version { get; set; } + + public override string Extension + { + get { return ".xml"; } + } + + public override string Name + { + get { return "D-Cinema SMPTE 2007"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + string xmlAsString = sb.ToString().Trim(); + + if (xmlAsString.Contains("http://www.smpte-ra.org/schemas/428-7/2010/DCST")) + return false; + + if (xmlAsString.Contains(" 0; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + return false; + } + } + else + { + return false; + } + } + + private static string RemoveSubStationAlphaFormatting(string s) + { + int indexOfBegin = s.IndexOf('{'); + while (indexOfBegin >= 0 && s.IndexOf('}') > indexOfBegin) + { + int indexOfEnd = s.IndexOf('}'); + s = s.Remove(indexOfBegin, (indexOfEnd - indexOfBegin) + 1); + indexOfBegin = s.IndexOf('{'); + } + return s; + } + + public override string ToText(Subtitle subtitle, string title) + { + var ss = Configuration.Settings.SubtitleSettings; + + if (!string.IsNullOrEmpty(ss.CurrentDCinemaEditRate)) + { + string[] temp = ss.CurrentDCinemaEditRate.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + double d1, d2; + if (temp.Length == 2 && double.TryParse(temp[0], out d1) && double.TryParse(temp[1], out d2)) + _frameRate = d1 / d2; + } + + string xmlStructure = + "" + Environment.NewLine + + " urn:uuid:7be835a3-cfb4-43d0-bb4b-f0b4c95e962e" + Environment.NewLine + + " " + Environment.NewLine + + " This is a subtitle file" + Environment.NewLine + + " 2012-06-26T12:33:59.000-00:00" + Environment.NewLine + + " 1" + Environment.NewLine + + " en" + Environment.NewLine + + " 25 1" + Environment.NewLine + + " 25" + Environment.NewLine + + " 00:00:00:00 " + Environment.NewLine + + " urn:uuid:3dec6dc0-39d0-498d-97d0-928d2eb78391" + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + ""; + + var xml = new XmlDocument(); + xml.LoadXml(xmlStructure); + xml.PreserveWhitespace = true; + var nsmgr = new XmlNamespaceManager(xml.NameTable); + nsmgr.AddNamespace("dcst", xml.DocumentElement.NamespaceURI); + + if (string.IsNullOrEmpty(ss.CurrentDCinemaMovieTitle)) + ss.CurrentDCinemaMovieTitle = title; + + if (ss.CurrentDCinemaFontSize == 0 || string.IsNullOrEmpty(ss.CurrentDCinemaFontEffect)) + Configuration.Settings.SubtitleSettings.InitializeDCinameSettings(true); + + xml.DocumentElement.SelectSingleNode("dcst:ContentTitleText", nsmgr).InnerText = ss.CurrentDCinemaMovieTitle; + if (string.IsNullOrEmpty(ss.CurrentDCinemaSubtitleId) || !ss.CurrentDCinemaSubtitleId.StartsWith("urn:uuid:")) + ss.CurrentDCinemaSubtitleId = "urn:uuid:" + Guid.NewGuid(); + xml.DocumentElement.SelectSingleNode("dcst:Id", nsmgr).InnerText = ss.CurrentDCinemaSubtitleId; + xml.DocumentElement.SelectSingleNode("dcst:ReelNumber", nsmgr).InnerText = ss.CurrentDCinemaReelNumber; + xml.DocumentElement.SelectSingleNode("dcst:IssueDate", nsmgr).InnerText = ss.CurrentDCinemaIssueDate; + if (string.IsNullOrEmpty(ss.CurrentDCinemaLanguage)) + ss.CurrentDCinemaLanguage = "en"; + xml.DocumentElement.SelectSingleNode("dcst:Language", nsmgr).InnerText = ss.CurrentDCinemaLanguage; + if (ss.CurrentDCinemaEditRate == null && ss.CurrentDCinemaTimeCodeRate == null) + { + if (Configuration.Settings.General.CurrentFrameRate == 24) + { + ss.CurrentDCinemaEditRate = "24 1"; + ss.CurrentDCinemaTimeCodeRate = "24"; + } + else + { + ss.CurrentDCinemaEditRate = "25 1"; + ss.CurrentDCinemaTimeCodeRate = "25"; + } + } + xml.DocumentElement.SelectSingleNode("dcst:EditRate", nsmgr).InnerText = ss.CurrentDCinemaEditRate; + xml.DocumentElement.SelectSingleNode("dcst:TimeCodeRate", nsmgr).InnerText = ss.CurrentDCinemaTimeCodeRate; + if (string.IsNullOrEmpty(ss.CurrentDCinemaStartTime)) + ss.CurrentDCinemaStartTime = "00:00:00:00"; + xml.DocumentElement.SelectSingleNode("dcst:StartTime", nsmgr).InnerText = ss.CurrentDCinemaStartTime; + xml.DocumentElement.SelectSingleNode("dcst:LoadFont", nsmgr).InnerText = ss.CurrentDCinemaFontUri; + int fontSize = ss.CurrentDCinemaFontSize; + string loadedFontId = "Font1"; + if (!string.IsNullOrEmpty(ss.CurrentDCinemaFontId)) + loadedFontId = ss.CurrentDCinemaFontId; + xml.DocumentElement.SelectSingleNode("dcst:LoadFont", nsmgr).Attributes["ID"].Value = loadedFontId; + xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["Size"].Value = fontSize.ToString(); + xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["Color"].Value = "FF" + Utilities.ColorToHex(ss.CurrentDCinemaFontColor).TrimStart('#').ToUpper(); + xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["ID"].Value = loadedFontId; + xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["Effect"].Value = ss.CurrentDCinemaFontEffect; + xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["EffectColor"].Value = "FF" + Utilities.ColorToHex(ss.CurrentDCinemaFontEffectColor).TrimStart('#').ToUpper(); + + XmlNode mainListFont = xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr); + int no = 0; + foreach (Paragraph p in subtitle.Paragraphs) + { + if (p.Text != null) + { + XmlNode subNode = xml.CreateElement("dcst:Subtitle", "dcst"); + + XmlAttribute id = xml.CreateAttribute("SpotNumber"); + id.InnerText = (no + 1).ToString(); + subNode.Attributes.Append(id); + + XmlAttribute fadeUpTime = xml.CreateAttribute("FadeUpTime"); + fadeUpTime.InnerText = "00:00:00:00"; //Configuration.Settings.SubtitleSettings.DCinemaFadeUpDownTime.ToString(); + subNode.Attributes.Append(fadeUpTime); + + XmlAttribute fadeDownTime = xml.CreateAttribute("FadeDownTime"); + fadeDownTime.InnerText = "00:00:00:00"; //Configuration.Settings.SubtitleSettings.DCinemaFadeUpDownTime.ToString(); + subNode.Attributes.Append(fadeDownTime); + + XmlAttribute start = xml.CreateAttribute("TimeIn"); + start.InnerText = ConvertToTimeString(p.StartTime); + subNode.Attributes.Append(start); + + XmlAttribute end = xml.CreateAttribute("TimeOut"); + end.InnerText = ConvertToTimeString(p.EndTime); + subNode.Attributes.Append(end); + + bool alignLeft = p.Text.StartsWith("{\\a1}") || p.Text.StartsWith("{\\a5}") || p.Text.StartsWith("{\\a9}") || // sub station alpha + p.Text.StartsWith("{\\an1}") || p.Text.StartsWith("{\\an4}") || p.Text.StartsWith("{\\an7}"); // advanced sub station alpha + + bool alignRight = p.Text.StartsWith("{\\a3}") || p.Text.StartsWith("{\\a7}") || p.Text.StartsWith("{\\a11}") || // sub station alpha + p.Text.StartsWith("{\\an3}") || p.Text.StartsWith("{\\an6}") || p.Text.StartsWith("{\\an9}"); // advanced sub station alpha + + bool alignVTop = p.Text.StartsWith("{\\a5}") || p.Text.StartsWith("{\\a6}") || p.Text.StartsWith("{\\a7}") || // sub station alpha + p.Text.StartsWith("{\\an7}") || p.Text.StartsWith("{\\an8}") || p.Text.StartsWith("{\\an9}"); // advanced sub station alpha + + bool alignVCenter = p.Text.StartsWith("{\\a9}") || p.Text.StartsWith("{\\a10}") || p.Text.StartsWith("{\\a11}") || // sub station alpha + p.Text.StartsWith("{\\an4}") || p.Text.StartsWith("{\\an5}") || p.Text.StartsWith("{\\an6}"); // advanced sub station alpha + + // remove styles for display text (except italic) + string text = RemoveSubStationAlphaFormatting(p.Text); + + var lines = text.SplitToLines(); + int vPos = 1 + lines.Length * 7; + int vPosFactor = (int)Math.Round(fontSize / 7.4); + if (alignVTop) + { + vPos = Configuration.Settings.SubtitleSettings.DCinemaBottomMargin; // Bottom margin is normally 8 + } + else if (alignVCenter) + { + vPos = (int)Math.Round((lines.Length * vPosFactor * -1) / 2.0); + } + else + { + vPos = (lines.Length * vPosFactor) - vPosFactor + Configuration.Settings.SubtitleSettings.DCinemaBottomMargin; // Bottom margin is normally 8 + } + + bool isItalic = false; + int fontNo = 0; + Stack fontColors = new Stack(); + foreach (string line in lines) + { + XmlNode textNode = xml.CreateElement("dcst:Text", "dcst"); + + XmlAttribute vPosition = xml.CreateAttribute("Vposition"); + vPosition.InnerText = vPos.ToString(); + textNode.Attributes.Append(vPosition); + + XmlAttribute vAlign = xml.CreateAttribute("Valign"); + if (alignVTop) + vAlign.InnerText = "top"; + else if (alignVCenter) + vAlign.InnerText = "center"; + else + vAlign.InnerText = "bottom"; + textNode.Attributes.Append(vAlign); textNode.Attributes.Append(vAlign); + + XmlAttribute hAlign = xml.CreateAttribute("Halign"); + if (alignLeft) + hAlign.InnerText = "left"; + else if (alignRight) + hAlign.InnerText = "right"; + else + hAlign.InnerText = "center"; + textNode.Attributes.Append(hAlign); + + XmlAttribute direction = xml.CreateAttribute("Direction"); + direction.InnerText = "ltr"; + textNode.Attributes.Append(direction); + + int i = 0; + var txt = new StringBuilder(); + var html = new StringBuilder(); + XmlNode nodeTemp = xml.CreateElement("temp"); + while (i < line.Length) + { + if (!isItalic && line.Substring(i).StartsWith("")) + { + if (txt.Length > 0) + { + nodeTemp.InnerText = txt.ToString(); + html.Append(nodeTemp.InnerXml); + txt = new StringBuilder(); + } + isItalic = true; + i += 2; + } + else if (isItalic && line.Substring(i).StartsWith("")) + { + if (txt.Length > 0) + { + XmlNode fontNode = xml.CreateElement("dcst:Font", "dcst"); + + XmlAttribute italic = xml.CreateAttribute("Italic"); + italic.InnerText = "yes"; + fontNode.Attributes.Append(italic); + + if (line.Length > i + 5 && line.Substring(i + 4).StartsWith("")) + { + XmlAttribute fontColor = xml.CreateAttribute("Color"); + fontColor.InnerText = fontColors.Pop(); + fontNode.Attributes.Append(fontColor); + fontNo--; + i += 7; + } + + fontNode.InnerText = HtmlUtil.RemoveHtmlTags(txt.ToString()); + html.Append(fontNode.OuterXml); + txt = new StringBuilder(); + } + isItalic = false; + i += 3; + } + else if (line.Substring(i).StartsWith(" 0 && line.Substring(i).StartsWith("")) + { + if (txt.Length > 0) + { + XmlNode fontNode = xml.CreateElement("dcst:Font", "dcst"); + + XmlAttribute fontColor = xml.CreateAttribute("Color"); + fontColor.InnerText = fontColors.Pop(); + fontNode.Attributes.Append(fontColor); + + if (line.Length > i + 9 && line.Substring(i + 7).StartsWith("")) + { + XmlAttribute italic = xml.CreateAttribute("Italic"); + italic.InnerText = "yes"; + fontNode.Attributes.Append(italic); + isItalic = false; + i += 4; + } + + fontNode.InnerText = HtmlUtil.RemoveHtmlTags(txt.ToString()); + html.Append(fontNode.OuterXml); + txt = new StringBuilder(); + } + fontNo--; + i += 6; + } + else + { + txt.Append(line[i]); + } + i++; + } + + if (fontNo > 0) + { + if (txt.Length > 0) + { + XmlNode fontNode = xml.CreateElement("dcst:Font", "dcst"); + + XmlAttribute fontColor = xml.CreateAttribute("Color"); + fontColor.InnerText = fontColors.Peek(); + fontNode.Attributes.Append(fontColor); + + if (isItalic) + { + XmlAttribute italic = xml.CreateAttribute("Italic"); + italic.InnerText = "yes"; + fontNode.Attributes.Append(italic); + } + + fontNode.InnerText = HtmlUtil.RemoveHtmlTags(txt.ToString()); + html.Append(fontNode.OuterXml); + } + else if (html.Length > 0 && html.ToString().StartsWith("" + html.ToString().Replace("dcst:Font", "Font") + ""); + XmlNode fontNode = xml.CreateElement("dcst:Font"); + fontNode.InnerXml = temp.DocumentElement.SelectSingleNode("Font").InnerXml; + foreach (XmlAttribute a in temp.DocumentElement.SelectSingleNode("Font").Attributes) + { + XmlAttribute newA = xml.CreateAttribute(a.Name); + newA.InnerText = a.InnerText; + fontNode.Attributes.Append(newA); + } + + XmlAttribute fontColor = xml.CreateAttribute("Color"); + fontColor.InnerText = fontColors.Peek(); + fontNode.Attributes.Append(fontColor); + + html = new StringBuilder(); + html.Append(fontNode.OuterXml); + } + } + else if (isItalic) + { + if (txt.Length > 0) + { + XmlNode fontNode = xml.CreateElement("dcst:Font", "dcst"); + + XmlAttribute italic = xml.CreateAttribute("Italic"); + italic.InnerText = "yes"; + fontNode.Attributes.Append(italic); + + fontNode.InnerText = HtmlUtil.RemoveHtmlTags(line); + html.Append(fontNode.OuterXml); + } + } + else + { + if (txt.Length > 0) + { + nodeTemp.InnerText = txt.ToString(); + html.Append(nodeTemp.InnerXml); + } + } + textNode.InnerXml = html.ToString(); + + subNode.AppendChild(textNode); + if (alignVTop) + vPos += vPosFactor; + else + vPos -= vPosFactor; + } + if (subNode.InnerXml.Length == 0) + { // Empty text is just one space + XmlNode textNode = xml.CreateElement("dcst:Text", "dcst"); + textNode.InnerXml = " "; + subNode.AppendChild(textNode); + + XmlAttribute vPosition = xml.CreateAttribute("Vposition"); + vPosition.InnerText = vPos.ToString(); + textNode.Attributes.Append(vPosition); + + XmlAttribute vAlign = xml.CreateAttribute("Valign"); + vAlign.InnerText = "bottom"; + textNode.Attributes.Append(vAlign); + } + mainListFont.AppendChild(subNode); + no++; + } + } + string result = ToUtf8XmlString(xml).Replace("encoding=\"utf-8\"", "encoding=\"UTF-8\"").Replace(" xmlns:dcst=\"dcst\"", string.Empty); + + const string res = "Nikse.SubtitleEdit.Resources.SMPTE-428-7-2007-DCST.xsd.gz"; + System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly(); + Stream strm = asm.GetManifestResourceStream(res); + if (strm != null) + { + try + { + var xmld = new XmlDocument(); + var rdr = new StreamReader(strm); + var zip = new GZipStream(rdr.BaseStream, CompressionMode.Decompress); + xmld.LoadXml(result); + using (var xr = XmlReader.Create(zip)) + { + xmld.Schemas.Add(null, xr); + xmld.Validate(ValidationCallBack); + } + } + catch (Exception exception) + { + if (!BatchMode) + System.Windows.Forms.MessageBox.Show("SMPTE-428-7-2007-DCST.xsd: " + exception.Message); + } + } + return result; + } + + private void ValidationCallBack(object sender, ValidationEventArgs e) + { + throw new Exception(e.Message); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + var xml = new XmlDocument(); + xml.XmlResolver = null; + xml.LoadXml(sb.ToString().Replace(" 0 && lastVPosition.Length > 0) + pText.AppendLine(); + lastVPosition = vPosition; + } + } + bool alignLeft = false; + bool alignRight = false; + bool alignVTop = false; + bool alignVCenter = false; + if (innerNode.Attributes["Halign"] != null) + { + string hAlign = innerNode.Attributes["Halign"].InnerText; + if (hAlign == "left") + alignLeft = true; + else if (hAlign == "right") + alignRight = true; + } + if (innerNode.Attributes["Valign"] != null) + { + string hAlign = innerNode.Attributes["Valign"].InnerText; + if (hAlign == "top") + alignVTop = true; + else if (hAlign == "center") + alignVCenter = true; + } + if (alignLeft || alignRight || alignVCenter || alignVTop) + { + if (!pText.ToString().StartsWith("{\\an")) + { + string pre = string.Empty; + if (alignVTop) + { + if (alignLeft) + pre = "{\\an7}"; + else if (alignRight) + pre = "{\\an9}"; + else + pre = "{\\an8}"; + } + else if (alignVCenter) + { + if (alignLeft) + pre = "{\\an4}"; + else if (alignRight) + pre = "{\\an6}"; + else + pre = "{\\an5}"; + } + else + { + if (alignLeft) + pre = "{\\an1}"; + else if (alignRight) + pre = "{\\an3}"; + } + string temp = pre + pText; + pText = new StringBuilder(); + pText.Append(temp); + } + } + + if (innerNode.ChildNodes.Count == 0) + { + pText.Append(innerNode.InnerText); + } + else + { + foreach (XmlNode innerInnerNode in innerNode) + { + if (innerInnerNode.Name == "Font" && innerInnerNode.Attributes["Italic"] != null && + innerInnerNode.Attributes["Italic"].InnerText.Equals("yes", StringComparison.OrdinalIgnoreCase)) + { + if (innerInnerNode.Attributes["Color"] != null) + pText.Append("" + innerInnerNode.InnerText + ""); + else + pText.Append("" + innerInnerNode.InnerText + ""); + } + else if (innerInnerNode.Name == "Font" && innerInnerNode.Attributes["Color"] != null) + { + if (innerInnerNode.Attributes["Italic"] != null && innerInnerNode.Attributes["Italic"].InnerText.Equals("yes", StringComparison.OrdinalIgnoreCase)) + pText.Append("" + innerInnerNode.InnerText + ""); + else + pText.Append("" + innerInnerNode.InnerText + ""); + } + else + { + pText.Append(innerInnerNode.InnerText); + } + } + } + break; + default: + pText.Append(innerNode.InnerText); + break; + } + } + string start = node.Attributes["TimeIn"].InnerText; + string end = node.Attributes["TimeOut"].InnerText; + + if (node.ParentNode.Name == "Font" && node.ParentNode.Attributes["Italic"] != null && node.ParentNode.Attributes["Italic"].InnerText.Equals("yes", StringComparison.OrdinalIgnoreCase) && + !pText.ToString().Contains("")) + { + string text = pText.ToString(); + if (text.StartsWith("{\\an") && text.Length > 6) + text = text.Insert(6, "") + ""; + else + text = "" + text + ""; + pText = new StringBuilder(text); + } + + subtitle.Paragraphs.Add(new Paragraph(GetTimeCode(start), GetTimeCode(end), pText.ToString())); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + _errorCount++; + } + } + + if (subtitle.Paragraphs.Count > 0) + subtitle.Header = xml.OuterXml; // save id/language/font for later use + + subtitle.Renumber(); + } + + private static string GetColorStringForDCinema(string p) + { + string s = p.ToUpper().Trim(); + if (s.Replace("#", string.Empty). + Replace("0", string.Empty). + Replace("1", string.Empty). + Replace("2", string.Empty). + Replace("3", string.Empty). + Replace("4", string.Empty). + Replace("5", string.Empty). + Replace("6", string.Empty). + Replace("7", string.Empty). + Replace("8", string.Empty). + Replace("9", string.Empty). + Replace("A", string.Empty). + Replace("B", string.Empty). + Replace("C", string.Empty). + Replace("D", string.Empty). + Replace("E", string.Empty). + Replace("F", string.Empty).Length == 0) + { + return s.TrimStart('#'); + } + else + { + return p; + } + } + + private static string GetColorStringFromDCinema(string p) + { + string s = p.ToLower().Trim(); + if (s.Replace("#", string.Empty). + Replace("0", string.Empty). + Replace("1", string.Empty). + Replace("2", string.Empty). + Replace("3", string.Empty). + Replace("4", string.Empty). + Replace("5", string.Empty). + Replace("6", string.Empty). + Replace("7", string.Empty). + Replace("8", string.Empty). + Replace("9", string.Empty). + Replace("a", string.Empty). + Replace("b", string.Empty). + Replace("c", string.Empty). + Replace("d", string.Empty). + Replace("e", string.Empty). + Replace("f", string.Empty).Length == 0) + { + if (s.StartsWith('#')) + return s; + else + return "#" + s; + } + else + { + return p; + } + } + + private TimeCode GetTimeCode(string s) + { + string[] parts = s.Split(new char[] { ':', '.', ',' }); + + int milliseconds = (int)Math.Round(int.Parse(parts[3]) * (TimeCode.BaseUnit / _frameRate)); + if (milliseconds > 999) + milliseconds = 999; + + return new TimeCode(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), milliseconds); + } + + private string ConvertToTimeString(TimeCode time) + { + return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", time.Hours, time.Minutes, time.Seconds, DCinemaSmpte2010.MsToFramesMaxFrameRate(time.Milliseconds, _frameRate)); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/DCinemaSmpte2010.cs b/libse/SubtitleFormats/DCinemaSmpte2010.cs new file mode 100644 index 000000000..bc8dd5633 --- /dev/null +++ b/libse/SubtitleFormats/DCinemaSmpte2010.cs @@ -0,0 +1,788 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Text; +using System.Xml; +using System.Xml.Schema; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class DCinemaSmpte2010 : SubtitleFormat + { + // + // + // urn:uuid:7be835a3-cfb4-43d0-bb4b-f0b4c95e962e + // 2001, A Space Odissey + // This is a subtitle file + // 2012-06-26T12:33:59.000-00:00 + // 1 + // fr + // 25 1 + // 25 + // 00:00:00:00 + // urn:uuid:3dec6dc0-39d0-498d-97d0-928d2eb78391 + // + // + // Hallo + // + // + + private double _frameRate = 24; + + public int Version { get; set; } + + public override string Extension + { + get { return ".xml"; } + } + + public override string Name + { + get { return "D-Cinema SMPTE 2010"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + string xmlAsString = sb.ToString().Trim(); + + if (xmlAsString.Contains("http://www.smpte-ra.org/schemas/428-7/2007/DCST")) + return false; + + if (xmlAsString.Contains(" 0; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + return false; + } + } + else + { + return false; + } + } + + private static string RemoveSubStationAlphaFormatting(string s) + { + int indexOfBegin = s.IndexOf('{'); + while (indexOfBegin >= 0 && s.IndexOf('}') > indexOfBegin) + { + int indexOfEnd = s.IndexOf('}'); + s = s.Remove(indexOfBegin, (indexOfEnd - indexOfBegin) + 1); + indexOfBegin = s.IndexOf('{'); + } + return s; + } + + public override string ToText(Subtitle subtitle, string title) + { + var ss = Configuration.Settings.SubtitleSettings; + + if (!string.IsNullOrEmpty(ss.CurrentDCinemaEditRate)) + { + string[] temp = ss.CurrentDCinemaEditRate.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + double d1, d2; + if (temp.Length == 2 && double.TryParse(temp[0], out d1) && double.TryParse(temp[1], out d2)) + _frameRate = d1 / d2; + } + + string xmlStructure = + "" + Environment.NewLine + + " urn:uuid:7be835a3-cfb4-43d0-bb4b-f0b4c95e962e" + Environment.NewLine + + " " + Environment.NewLine + + " This is a subtitle file" + Environment.NewLine + + " 2012-06-26T12:33:59.000-00:00" + Environment.NewLine + + " 1" + Environment.NewLine + + " en" + Environment.NewLine + + " 25 1" + Environment.NewLine + + " 25" + Environment.NewLine + + " 00:00:00:00 " + Environment.NewLine + + " urn:uuid:3dec6dc0-39d0-498d-97d0-928d2eb78391" + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + ""; + + var xml = new XmlDocument(); + xml.LoadXml(xmlStructure); + xml.PreserveWhitespace = true; + var nsmgr = new XmlNamespaceManager(xml.NameTable); + nsmgr.AddNamespace("dcst", xml.DocumentElement.NamespaceURI); + + if (string.IsNullOrEmpty(ss.CurrentDCinemaMovieTitle)) + ss.CurrentDCinemaMovieTitle = title; + + if (ss.CurrentDCinemaFontSize == 0 || string.IsNullOrEmpty(ss.CurrentDCinemaFontEffect)) + Configuration.Settings.SubtitleSettings.InitializeDCinameSettings(true); + + xml.DocumentElement.SelectSingleNode("dcst:ContentTitleText", nsmgr).InnerText = ss.CurrentDCinemaMovieTitle; + if (string.IsNullOrEmpty(ss.CurrentDCinemaSubtitleId) || !ss.CurrentDCinemaSubtitleId.StartsWith("urn:uuid:")) + ss.CurrentDCinemaSubtitleId = "urn:uuid:" + Guid.NewGuid(); + xml.DocumentElement.SelectSingleNode("dcst:Id", nsmgr).InnerText = ss.CurrentDCinemaSubtitleId; + xml.DocumentElement.SelectSingleNode("dcst:ReelNumber", nsmgr).InnerText = ss.CurrentDCinemaReelNumber; + xml.DocumentElement.SelectSingleNode("dcst:IssueDate", nsmgr).InnerText = ss.CurrentDCinemaIssueDate; + if (string.IsNullOrEmpty(ss.CurrentDCinemaLanguage)) + ss.CurrentDCinemaLanguage = "en"; + xml.DocumentElement.SelectSingleNode("dcst:Language", nsmgr).InnerText = ss.CurrentDCinemaLanguage; + if (ss.CurrentDCinemaEditRate == null && ss.CurrentDCinemaTimeCodeRate == null) + { + if (Configuration.Settings.General.CurrentFrameRate == 24) + { + ss.CurrentDCinemaEditRate = "24 1"; + ss.CurrentDCinemaTimeCodeRate = "24"; + } + else + { + ss.CurrentDCinemaEditRate = "25 1"; + ss.CurrentDCinemaTimeCodeRate = "25"; + } + } + xml.DocumentElement.SelectSingleNode("dcst:EditRate", nsmgr).InnerText = ss.CurrentDCinemaEditRate; + xml.DocumentElement.SelectSingleNode("dcst:TimeCodeRate", nsmgr).InnerText = ss.CurrentDCinemaTimeCodeRate; + if (string.IsNullOrEmpty(ss.CurrentDCinemaStartTime)) + ss.CurrentDCinemaStartTime = "00:00:00:00"; + xml.DocumentElement.SelectSingleNode("dcst:StartTime", nsmgr).InnerText = ss.CurrentDCinemaStartTime; + xml.DocumentElement.SelectSingleNode("dcst:LoadFont", nsmgr).InnerText = ss.CurrentDCinemaFontUri; + int fontSize = ss.CurrentDCinemaFontSize; + string loadedFontId = "Font1"; + if (!string.IsNullOrEmpty(ss.CurrentDCinemaFontId)) + loadedFontId = ss.CurrentDCinemaFontId; + xml.DocumentElement.SelectSingleNode("dcst:LoadFont", nsmgr).Attributes["ID"].Value = loadedFontId; + xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["Size"].Value = fontSize.ToString(); + xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["Color"].Value = "FF" + Utilities.ColorToHex(ss.CurrentDCinemaFontColor).TrimStart('#').ToUpper(); + xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["ID"].Value = loadedFontId; + xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["Effect"].Value = ss.CurrentDCinemaFontEffect; + xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["EffectColor"].Value = "FF" + Utilities.ColorToHex(ss.CurrentDCinemaFontEffectColor).TrimStart('#').ToUpper(); + + XmlNode mainListFont = xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr); + int no = 0; + foreach (Paragraph p in subtitle.Paragraphs) + { + if (p.Text != null) + { + XmlNode subNode = xml.CreateElement("dcst:Subtitle", "dcst"); + + XmlAttribute id = xml.CreateAttribute("SpotNumber"); + id.InnerText = (no + 1).ToString(); + subNode.Attributes.Append(id); + + XmlAttribute fadeUpTime = xml.CreateAttribute("FadeUpTime"); + fadeUpTime.InnerText = "00:00:00:00"; //Configuration.Settings.SubtitleSettings.DCinemaFadeUpDownTime.ToString(); + subNode.Attributes.Append(fadeUpTime); + + XmlAttribute fadeDownTime = xml.CreateAttribute("FadeDownTime"); + fadeDownTime.InnerText = "00:00:00:00"; //Configuration.Settings.SubtitleSettings.DCinemaFadeUpDownTime.ToString(); + subNode.Attributes.Append(fadeDownTime); + + XmlAttribute start = xml.CreateAttribute("TimeIn"); + start.InnerText = ConvertToTimeString(p.StartTime); + subNode.Attributes.Append(start); + + XmlAttribute end = xml.CreateAttribute("TimeOut"); + end.InnerText = ConvertToTimeString(p.EndTime); + subNode.Attributes.Append(end); + + bool alignLeft = p.Text.StartsWith("{\\a1}") || p.Text.StartsWith("{\\a5}") || p.Text.StartsWith("{\\a9}") || // sub station alpha + p.Text.StartsWith("{\\an1}") || p.Text.StartsWith("{\\an4}") || p.Text.StartsWith("{\\an7}"); // advanced sub station alpha + + bool alignRight = p.Text.StartsWith("{\\a3}") || p.Text.StartsWith("{\\a7}") || p.Text.StartsWith("{\\a11}") || // sub station alpha + p.Text.StartsWith("{\\an3}") || p.Text.StartsWith("{\\an6}") || p.Text.StartsWith("{\\an9}"); // advanced sub station alpha + + bool alignVTop = p.Text.StartsWith("{\\a5}") || p.Text.StartsWith("{\\a6}") || p.Text.StartsWith("{\\a7}") || // sub station alpha + p.Text.StartsWith("{\\an7}") || p.Text.StartsWith("{\\an8}") || p.Text.StartsWith("{\\an9}"); // advanced sub station alpha + + bool alignVCenter = p.Text.StartsWith("{\\a9}") || p.Text.StartsWith("{\\a10}") || p.Text.StartsWith("{\\a11}") || // sub station alpha + p.Text.StartsWith("{\\an4}") || p.Text.StartsWith("{\\an5}") || p.Text.StartsWith("{\\an6}"); // advanced sub station alpha + + // remove styles for display text (except italic) + string text = RemoveSubStationAlphaFormatting(p.Text); + + var lines = text.SplitToLines(); + int vPos = 1 + lines.Length * 7; + int vPosFactor = (int)Math.Round(fontSize / 7.4); + if (alignVTop) + { + vPos = Configuration.Settings.SubtitleSettings.DCinemaBottomMargin; // Bottom margin is normally 8 + } + else if (alignVCenter) + { + vPos = (int)Math.Round((lines.Length * vPosFactor * -1) / 2.0); + } + else + { + vPos = (lines.Length * vPosFactor) - vPosFactor + Configuration.Settings.SubtitleSettings.DCinemaBottomMargin; // Bottom margin is normally 8 + } + + bool isItalic = false; + int fontNo = 0; + Stack fontColors = new Stack(); + foreach (string line in lines) + { + XmlNode textNode = xml.CreateElement("dcst:Text", "dcst"); + + XmlAttribute vPosition = xml.CreateAttribute("Vposition"); + vPosition.InnerText = vPos.ToString(); + textNode.Attributes.Append(vPosition); + + XmlAttribute vAlign = xml.CreateAttribute("Valign"); + if (alignVTop) + vAlign.InnerText = "top"; + else if (alignVCenter) + vAlign.InnerText = "center"; + else + vAlign.InnerText = "bottom"; + textNode.Attributes.Append(vAlign); textNode.Attributes.Append(vAlign); + + XmlAttribute hAlign = xml.CreateAttribute("Halign"); + if (alignLeft) + hAlign.InnerText = "left"; + else if (alignRight) + hAlign.InnerText = "right"; + else + hAlign.InnerText = "center"; + textNode.Attributes.Append(hAlign); + + XmlAttribute direction = xml.CreateAttribute("Direction"); + direction.InnerText = "ltr"; + textNode.Attributes.Append(direction); + + int i = 0; + var txt = new StringBuilder(); + var html = new StringBuilder(); + XmlNode nodeTemp = xml.CreateElement("temp"); + while (i < line.Length) + { + if (!isItalic && line.Substring(i).StartsWith("")) + { + if (txt.Length > 0) + { + nodeTemp.InnerText = txt.ToString(); + html.Append(nodeTemp.InnerXml); + txt = new StringBuilder(); + } + isItalic = true; + i += 2; + } + else if (isItalic && line.Substring(i).StartsWith("")) + { + if (txt.Length > 0) + { + XmlNode fontNode = xml.CreateElement("dcst:Font", "dcst"); + + XmlAttribute italic = xml.CreateAttribute("Italic"); + italic.InnerText = "yes"; + fontNode.Attributes.Append(italic); + + if (line.Length > i + 5 && line.Substring(i + 4).StartsWith("")) + { + XmlAttribute fontColor = xml.CreateAttribute("Color"); + fontColor.InnerText = fontColors.Pop(); + fontNode.Attributes.Append(fontColor); + fontNo--; + i += 7; + } + + fontNode.InnerText = HtmlUtil.RemoveHtmlTags(txt.ToString()); + html.Append(fontNode.OuterXml); + txt = new StringBuilder(); + } + isItalic = false; + i += 3; + } + else if (line.Substring(i).StartsWith(" 0 && line.Substring(i).StartsWith("")) + { + if (txt.Length > 0) + { + XmlNode fontNode = xml.CreateElement("dcst:Font", "dcst"); + + XmlAttribute fontColor = xml.CreateAttribute("Color"); + fontColor.InnerText = fontColors.Pop(); + fontNode.Attributes.Append(fontColor); + + if (line.Length > i + 9 && line.Substring(i + 7).StartsWith("")) + { + XmlAttribute italic = xml.CreateAttribute("Italic"); + italic.InnerText = "yes"; + fontNode.Attributes.Append(italic); + isItalic = false; + i += 4; + } + + fontNode.InnerText = HtmlUtil.RemoveHtmlTags(txt.ToString()); + html.Append(fontNode.OuterXml); + txt = new StringBuilder(); + } + fontNo--; + i += 6; + } + else + { + txt.Append(line[i]); + } + i++; + } + + if (fontNo > 0) + { + if (txt.Length > 0) + { + XmlNode fontNode = xml.CreateElement("dcst:Font", "dcst"); + + XmlAttribute fontColor = xml.CreateAttribute("Color"); + fontColor.InnerText = fontColors.Peek(); + fontNode.Attributes.Append(fontColor); + + if (isItalic) + { + XmlAttribute italic = xml.CreateAttribute("Italic"); + italic.InnerText = "yes"; + fontNode.Attributes.Append(italic); + } + + fontNode.InnerText = HtmlUtil.RemoveHtmlTags(txt.ToString()); + html.Append(fontNode.OuterXml); + } + else if (html.Length > 0 && html.ToString().StartsWith("" + html.ToString().Replace("dcst:Font", "Font") + ""); + XmlNode fontNode = xml.CreateElement("dcst:Font"); + fontNode.InnerXml = temp.DocumentElement.SelectSingleNode("Font").InnerXml; + foreach (XmlAttribute a in temp.DocumentElement.SelectSingleNode("Font").Attributes) + { + XmlAttribute newA = xml.CreateAttribute(a.Name); + newA.InnerText = a.InnerText; + fontNode.Attributes.Append(newA); + } + + XmlAttribute fontColor = xml.CreateAttribute("Color"); + fontColor.InnerText = fontColors.Peek(); + fontNode.Attributes.Append(fontColor); + + html = new StringBuilder(); + html.Append(fontNode.OuterXml); + } + } + else if (isItalic) + { + if (txt.Length > 0) + { + XmlNode fontNode = xml.CreateElement("dcst:Font", "dcst"); + + XmlAttribute italic = xml.CreateAttribute("Italic"); + italic.InnerText = "yes"; + fontNode.Attributes.Append(italic); + + fontNode.InnerText = HtmlUtil.RemoveHtmlTags(line); + html.Append(fontNode.OuterXml); + } + } + else + { + if (txt.Length > 0) + { + nodeTemp.InnerText = txt.ToString(); + html.Append(nodeTemp.InnerXml); + } + } + textNode.InnerXml = html.ToString(); + + subNode.AppendChild(textNode); + if (alignVTop) + vPos += vPosFactor; + else + vPos -= vPosFactor; + } + if (subNode.InnerXml.Length == 0) + { // Empty text is just one space + XmlNode textNode = xml.CreateElement("dcst:Text", "dcst"); + textNode.InnerXml = " "; + subNode.AppendChild(textNode); + + XmlAttribute vPosition = xml.CreateAttribute("Vposition"); + vPosition.InnerText = vPos.ToString(); + textNode.Attributes.Append(vPosition); + + XmlAttribute vAlign = xml.CreateAttribute("Valign"); + vAlign.InnerText = "bottom"; + textNode.Attributes.Append(vAlign); + } + mainListFont.AppendChild(subNode); + no++; + } + } + string result = ToUtf8XmlString(xml).Replace("encoding=\"utf-8\"", "encoding=\"UTF-8\"").Replace(" xmlns:dcst=\"dcst\"", string.Empty); + + const string res = "Nikse.SubtitleEdit.Resources.SMPTE-428-7-2010-DCST.xsd.gz"; + System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly(); + Stream strm = asm.GetManifestResourceStream(res); + if (strm != null) + { + try + { + var xmld = new XmlDocument(); + var rdr = new StreamReader(strm); + var zip = new GZipStream(rdr.BaseStream, CompressionMode.Decompress); + xmld.LoadXml(result); + using (var xr = XmlReader.Create(zip)) + { + xmld.Schemas.Add(null, xr); + xmld.Validate(ValidationCallBack); + } + } + catch (Exception exception) + { + if (!BatchMode) + System.Windows.Forms.MessageBox.Show("SMPTE-428-7-2010-DCST.xsd: " + exception.Message); + } + } + return result; + } + + private void ValidationCallBack(object sender, ValidationEventArgs e) + { + throw new Exception(e.Message); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + var xml = new XmlDocument(); + xml.XmlResolver = null; + xml.LoadXml(sb.ToString().Replace(" 0 && lastVPosition.Length > 0) + pText.AppendLine(); + lastVPosition = vPosition; + } + } + bool alignLeft = false; + bool alignRight = false; + bool alignVTop = false; + bool alignVCenter = false; + if (innerNode.Attributes["Halign"] != null) + { + string hAlign = innerNode.Attributes["Halign"].InnerText; + if (hAlign == "left") + alignLeft = true; + else if (hAlign == "right") + alignRight = true; + } + if (innerNode.Attributes["Valign"] != null) + { + string hAlign = innerNode.Attributes["Valign"].InnerText; + if (hAlign == "top") + alignVTop = true; + else if (hAlign == "center") + alignVCenter = true; + } + if (alignLeft || alignRight || alignVCenter || alignVTop) + { + if (!pText.ToString().StartsWith("{\\an")) + { + string pre = string.Empty; + if (alignVTop) + { + if (alignLeft) + pre = "{\\an7}"; + else if (alignRight) + pre = "{\\an9}"; + else + pre = "{\\an8}"; + } + else if (alignVCenter) + { + if (alignLeft) + pre = "{\\an4}"; + else if (alignRight) + pre = "{\\an6}"; + else + pre = "{\\an5}"; + } + else + { + if (alignLeft) + pre = "{\\an1}"; + else if (alignRight) + pre = "{\\an3}"; + } + string temp = pre + pText; + pText = new StringBuilder(); + pText.Append(temp); + } + } + + if (innerNode.ChildNodes.Count == 0) + { + pText.Append(innerNode.InnerText); + } + else + { + foreach (XmlNode innerInnerNode in innerNode) + { + if (innerInnerNode.Name == "Font" && innerInnerNode.Attributes["Italic"] != null && + innerInnerNode.Attributes["Italic"].InnerText.Equals("yes", StringComparison.OrdinalIgnoreCase)) + { + if (innerInnerNode.Attributes["Color"] != null) + pText.Append("" + innerInnerNode.InnerText + ""); + else + pText.Append("" + innerInnerNode.InnerText + ""); + } + else if (innerInnerNode.Name == "Font" && innerInnerNode.Attributes["Color"] != null) + { + if (innerInnerNode.Attributes["Italic"] != null && innerInnerNode.Attributes["Italic"].InnerText.Equals("yes", StringComparison.OrdinalIgnoreCase)) + pText.Append("" + innerInnerNode.InnerText + ""); + else + pText.Append("" + innerInnerNode.InnerText + ""); + } + else + { + pText.Append(innerInnerNode.InnerText); + } + } + } + break; + default: + pText.Append(innerNode.InnerText); + break; + } + } + string start = node.Attributes["TimeIn"].InnerText; + string end = node.Attributes["TimeOut"].InnerText; + + if (node.ParentNode.Name == "Font" && node.ParentNode.Attributes["Italic"] != null && node.ParentNode.Attributes["Italic"].InnerText.Equals("yes", StringComparison.OrdinalIgnoreCase) && + !pText.ToString().Contains("")) + { + string text = pText.ToString(); + if (text.StartsWith("{\\an") && text.Length > 6) + text = text.Insert(6, "") + ""; + else + text = "" + text + ""; + pText = new StringBuilder(text); + } + + subtitle.Paragraphs.Add(new Paragraph(GetTimeCode(start), GetTimeCode(end), pText.ToString())); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + _errorCount++; + } + } + + if (subtitle.Paragraphs.Count > 0) + subtitle.Header = xml.OuterXml; // save id/language/font for later use + + subtitle.Renumber(); + } + + private static string GetColorStringForDCinema(string p) + { + string s = p.ToUpper().Trim(); + if (s.Replace("#", string.Empty). + Replace("0", string.Empty). + Replace("1", string.Empty). + Replace("2", string.Empty). + Replace("3", string.Empty). + Replace("4", string.Empty). + Replace("5", string.Empty). + Replace("6", string.Empty). + Replace("7", string.Empty). + Replace("8", string.Empty). + Replace("9", string.Empty). + Replace("A", string.Empty). + Replace("B", string.Empty). + Replace("C", string.Empty). + Replace("D", string.Empty). + Replace("E", string.Empty). + Replace("F", string.Empty).Length == 0) + { + return s.TrimStart('#'); + } + else + { + return p; + } + } + + private static string GetColorStringFromDCinema(string p) + { + string s = p.ToLower().Trim(); + if (s.Replace("#", string.Empty). + Replace("0", string.Empty). + Replace("1", string.Empty). + Replace("2", string.Empty). + Replace("3", string.Empty). + Replace("4", string.Empty). + Replace("5", string.Empty). + Replace("6", string.Empty). + Replace("7", string.Empty). + Replace("8", string.Empty). + Replace("9", string.Empty). + Replace("a", string.Empty). + Replace("b", string.Empty). + Replace("c", string.Empty). + Replace("d", string.Empty). + Replace("e", string.Empty). + Replace("f", string.Empty).Length == 0) + { + if (s.StartsWith('#')) + return s; + else + return "#" + s; + } + else + { + return p; + } + } + + private TimeCode GetTimeCode(string s) + { + string[] parts = s.Split(new char[] { ':', '.', ',' }); + + int milliseconds = (int)Math.Round(int.Parse(parts[3]) * (TimeCode.BaseUnit / _frameRate)); + if (milliseconds > 999) + milliseconds = 999; + + return new TimeCode(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), milliseconds); + } + + public static int MsToFramesMaxFrameRate(double milliseconds, double frameRate) + { + int frames = (int)Math.Round(milliseconds / (TimeCode.BaseUnit / frameRate)); + if (frames >= Configuration.Settings.General.CurrentFrameRate) + frames = (int)(frameRate - 0.01); + return frames; + } + + private string ConvertToTimeString(TimeCode time) + { + return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", time.Hours, time.Minutes, time.Seconds, MsToFramesMaxFrameRate(time.Milliseconds, _frameRate)); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/DigiBeta.cs b/libse/SubtitleFormats/DigiBeta.cs new file mode 100644 index 000000000..71dbfb037 --- /dev/null +++ b/libse/SubtitleFormats/DigiBeta.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class DigiBeta : SubtitleFormat + { + private static readonly Regex RegexTimeCode = new Regex(@"^\d\d \d\d \d\d \d\d\t\d\d \d\d \d\d \d\d\t", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "DigiBeta"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + Subtitle subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + //10 01 37 23 10 01 42 01 Makkhi (newline is TAB) + const string paragraphWriteFormat = "{0}\t{1}\t{2}"; + + StringBuilder sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.AppendLine(string.Format(paragraphWriteFormat, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), p.Text.Replace(Environment.NewLine, "\t"))); + } + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + var paragraph = new Paragraph(); + _errorCount = 0; + + subtitle.Paragraphs.Clear(); + foreach (string line in lines) + { + if (RegexTimeCode.IsMatch(line) && line.Length > 24) + { + string[] parts = line.Substring(0, 11).Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 4) + { + try + { + var start = DecodeTimeCode(parts); + parts = line.Substring(12, 11).Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + var end = DecodeTimeCode(parts); + paragraph = new Paragraph(); + paragraph.StartTime = start; + paragraph.EndTime = end; + + paragraph.Text = line.Substring(24).Trim().Replace("\t", Environment.NewLine); + subtitle.Paragraphs.Add(paragraph); + } + catch + { + _errorCount++; + } + } + } + } + subtitle.Renumber(); + } + + private static string EncodeTimeCode(TimeCode time) + { + return string.Format("{0:00} {1:00} {2:00} {3:00}", time.Hours, time.Minutes, time.Seconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + string hour = parts[0]; + string minutes = parts[1]; + string seconds = parts[2]; + string frames = parts[3]; + + return new TimeCode(int.Parse(hour), int.Parse(minutes), int.Parse(seconds), FramesToMillisecondsMax999(int.Parse(frames))); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/Dost.cs b/libse/SubtitleFormats/Dost.cs new file mode 100644 index 000000000..1cbc1c5ba --- /dev/null +++ b/libse/SubtitleFormats/Dost.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class Dost : SubtitleFormat + { + private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d\d\d\t\d\d:\d\d:\d\d:\d\d\t\d\d:\d\d:\d\d:\d\d\t", RegexOptions.Compiled); + + public override string Extension + { + get { return ".dost"; } + } + + public override string Name + { + get { return "DOST"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + + var sb = new StringBuilder(); + foreach (string line in lines) + sb.AppendLine(line); + if (!sb.ToString().Contains(Environment.NewLine + "NO\tINTIME")) + return false; + if (!sb.ToString().Contains("$FORMAT")) + return false; + + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + return "Not implemented"; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + //0001 01:25:59:21 01:26:00:20 0 0 BK02-total_0001.png 0 0 + Paragraph p = null; + subtitle.Paragraphs.Clear(); + _errorCount = 0; + foreach (string line in lines) + { + string s = line; + if (RegexTimeCodes.IsMatch(s)) + { + var temp = s.Split('\t'); + if (temp.Length > 7) + { + string start = temp[1]; + string end = temp[2]; + string text = temp[5]; + try + { + p = new Paragraph(DecodeTimeCode(start.Split(':')), DecodeTimeCode(end.Split(':')), text); + subtitle.Paragraphs.Add(p); + } + catch (Exception exception) + { + _errorCount++; + System.Diagnostics.Debug.WriteLine(exception.Message); + } + } + } + else if (line.StartsWith("$DROP=")) + { + s = s.Remove(0, "$DROP=".Length); + int frameRate; + if (int.TryParse(s, out frameRate)) + { + double f = frameRate / TimeCode.BaseUnit; + if (f > 10 && f < 500) + Configuration.Settings.General.CurrentFrameRate = f; + } + } + else if (string.IsNullOrWhiteSpace(line) || line.StartsWith('$')) + { + // skip empty lines or lines starting with $ + } + else if (!string.IsNullOrWhiteSpace(line) && p != null) + { + _errorCount++; + } + } + + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + //00:00:07:12 + string hour = parts[0]; + string minutes = parts[1]; + string seconds = parts[2]; + string frames = parts[3]; + + TimeCode tc = new TimeCode(int.Parse(hour), int.Parse(minutes), int.Parse(seconds), FramesToMillisecondsMax999(int.Parse(frames))); + return tc; + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/DvdStudioPro.cs b/libse/SubtitleFormats/DvdStudioPro.cs new file mode 100644 index 000000000..d72335f6c --- /dev/null +++ b/libse/SubtitleFormats/DvdStudioPro.cs @@ -0,0 +1,182 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class DvdStudioPro : SubtitleFormat + { + private static readonly Regex RegexTimeCodes = new Regex(@"^\d+:\d+:\d+:\d+\t,\t\d+:\d+:\d+:\d+\t,\t.*$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".STL"; } + } + + public override string Name + { + get { return "DVD Studio Pro"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string paragraphWriteFormat = "{0}\t,\t{1}\t,\t{2}\r\n"; + const string timeFormat = "{0:00}:{1:00}:{2:00}:{3:00}"; + const string header = @"$VertAlign = Bottom +$Bold = FALSE +$Underlined = FALSE +$Italic = 0 +$XOffset = 0 +$YOffset = -5 +$TextContrast = 15 +$Outline1Contrast = 15 +$Outline2Contrast = 13 +$BackgroundContrast = 0 +$ForceDisplay = FALSE +$FadeIn = 0 +$FadeOut = 0 +$HorzAlign = Center +"; + + var sb = new StringBuilder(); + sb.AppendLine(header); + foreach (Paragraph p in subtitle.Paragraphs) + { + double factor = (TimeCode.BaseUnit / Configuration.Settings.General.CurrentFrameRate); + string startTime = string.Format(timeFormat, p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, (int)Math.Round(p.StartTime.Milliseconds / factor)); + string endTime = string.Format(timeFormat, p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, (int)Math.Round(p.EndTime.Milliseconds / factor)); + sb.AppendFormat(paragraphWriteFormat, startTime, endTime, EncodeStyles(p.Text)); + } + return sb.ToString().Trim(); + } + + public static byte GetFrameFromMilliseconds(int milliseconds, double frameRate) + { + return (byte)Math.Round(milliseconds / (TimeCode.BaseUnit / frameRate)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + int number = 0; + foreach (string line in lines) + { + if (!string.IsNullOrWhiteSpace(line) && line[0] != '$') + { + if (RegexTimeCodes.Match(line).Success) + { + string[] threePart = line.Split(new[] { "\t,\t" }, StringSplitOptions.None); + var p = new Paragraph(); + if (threePart.Length == 3 && + GetTimeCode(p.StartTime, threePart[0]) && + GetTimeCode(p.EndTime, threePart[1])) + { + number++; + p.Number = number; + p.Text = threePart[2].TrimEnd().Replace(" | ", Environment.NewLine).Replace("|", Environment.NewLine); + p.Text = DecodeStyles(p.Text); + subtitle.Paragraphs.Add(p); + } + } + else + { + _errorCount++; + } + } + } + } + + internal static string DecodeStyles(string text) + { + var sb = new StringBuilder(); + bool italicOn = false; + bool boldOn = false; + bool skipNext = false; + for (int i = 0; i < text.Length; i++) + { + if (skipNext) + { + skipNext = false; + } + else + { + if (text.Substring(i).StartsWith("^I")) + { + if (!italicOn) + sb.Append(""); + else + sb.Append(""); + italicOn = !italicOn; + skipNext = true; + } + else if (text.Substring(i).StartsWith("^B")) + { + if (!boldOn) + sb.Append(""); + else + sb.Append(""); + boldOn = !boldOn; + skipNext = true; + } + else + { + sb.Append(text[i]); + } + } + } + return sb.ToString(); + } + + internal static string EncodeStyles(string text) + { + text = Utilities.RemoveSsaTags(text); + text = text.Replace("", "").Replace("", ""); + bool allItalic = text.StartsWith("") && text.EndsWith("") && Utilities.CountTagInText(text, "") == 1; + + text = text.Replace("", "^I"); + text = text.Replace("", "^I"); + text = text.Replace("", "^I"); + text = text.Replace("", "^I"); + + text = text.Replace("", "^B"); + text = text.Replace("", "^B"); + text = text.Replace("", "^B"); + text = text.Replace("", "^B"); + + if (allItalic) + return text.Replace(Environment.NewLine, "|^I"); + return text.Replace(Environment.NewLine, "|"); + } + + internal static bool GetTimeCode(TimeCode timeCode, string timeString) + { + try + { + string[] timeParts = timeString.Split(':'); + timeCode.Hours = int.Parse(timeParts[0]); + timeCode.Minutes = int.Parse(timeParts[1]); + timeCode.Seconds = int.Parse(timeParts[2]); + timeCode.Milliseconds = FramesToMillisecondsMax999(int.Parse(timeParts[3])); + return true; + } + catch + { + return false; + } + } + + } +} diff --git a/libse/SubtitleFormats/DvdStudioProSpace.cs b/libse/SubtitleFormats/DvdStudioProSpace.cs new file mode 100644 index 000000000..822bd5e26 --- /dev/null +++ b/libse/SubtitleFormats/DvdStudioProSpace.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class DvdStudioProSpace : SubtitleFormat + { + private static readonly Regex RegexTimeCodes = new Regex(@"^\d+:\d+:\d+:\d+ , \d+:\d+:\d+:\d+ , .*$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".STL"; } + } + + public override string Name + { + get { return "DVD Studio Pro with space"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string paragraphWriteFormat = "{0} , {1} , {2}\r\n"; + const string timeFormat = "{0:00}:{1:00}:{2:00}:{3:00}"; + const string header = @"$VertAlign = Bottom +$Bold = FALSE +$Underlined = FALSE +$Italic = 0 +$XOffset = 0 +$YOffset = -5 +$TextContrast = 15 +$Outline1Contrast = 15 +$Outline2Contrast = 13 +$BackgroundContrast = 0 +$ForceDisplay = FALSE +$FadeIn = 0 +$FadeOut = 0 +$HorzAlign = Center +"; + + var sb = new StringBuilder(); + sb.AppendLine(header); + foreach (Paragraph p in subtitle.Paragraphs) + { + double factor = (TimeCode.BaseUnit / Configuration.Settings.General.CurrentFrameRate); + string startTime = string.Format(timeFormat, p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, (int)Math.Round(p.StartTime.Milliseconds / factor)); + string endTime = string.Format(timeFormat, p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, (int)Math.Round(p.EndTime.Milliseconds / factor)); + sb.AppendFormat(paragraphWriteFormat, startTime, endTime, DvdStudioPro.EncodeStyles(p.Text)); + } + return sb.ToString().Trim(); + } + + public static byte GetFrameFromMilliseconds(int milliseconds, double frameRate) + { + return (byte)Math.Round(milliseconds / (TimeCode.BaseUnit / frameRate)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + int number = 0; + foreach (string line in lines) + { + if (!string.IsNullOrWhiteSpace(line) && line[0] != '$' && !line.StartsWith("//")) + { + if (RegexTimeCodes.Match(line).Success) + { + string[] toPart = line.Substring(0, 25).Split(new[] { " ," }, StringSplitOptions.None); + Paragraph p = new Paragraph(); + if (toPart.Length == 2 && + DvdStudioPro.GetTimeCode(p.StartTime, toPart[0]) && + DvdStudioPro.GetTimeCode(p.EndTime, toPart[1])) + { + number++; + p.Number = number; + string text = line.Substring(27).Trim(); + p.Text = text.Replace(" | ", Environment.NewLine).Replace("|", Environment.NewLine); + p.Text = DvdStudioPro.DecodeStyles(p.Text); + subtitle.Paragraphs.Add(p); + } + } + else + { + _errorCount++; + } + } + } + } + + } +} diff --git a/libse/SubtitleFormats/DvdStudioProSpaceOne.cs b/libse/SubtitleFormats/DvdStudioProSpaceOne.cs new file mode 100644 index 000000000..2b596f128 --- /dev/null +++ b/libse/SubtitleFormats/DvdStudioProSpaceOne.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class DvdStudioProSpaceOne : SubtitleFormat + { + private static readonly Regex RegexTimeCodes = new Regex(@"^\d+:\d+:\d+:\d+,\d+:\d+:\d+:\d+, .*$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".STL"; } + } + + public override string Name + { + get { return "DVD Studio Pro with one space"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string paragraphWriteFormat = "{0},{1}, {2}\r\n"; + const string timeFormat = "{0:00}:{1:00}:{2:00}:{3:00}"; + const string header = @"$VertAlign = Bottom +$Bold = FALSE +$Underlined = FALSE +$Italic = 0 +$XOffset = 0 +$YOffset = -5 +$TextContrast = 15 +$Outline1Contrast = 15 +$Outline2Contrast = 13 +$BackgroundContrast = 0 +$ForceDisplay = FALSE +$FadeIn = 0 +$FadeOut = 0 +$HorzAlign = Center +"; + + var sb = new StringBuilder(); + sb.AppendLine(header); + foreach (Paragraph p in subtitle.Paragraphs) + { + double factor = (TimeCode.BaseUnit / Configuration.Settings.General.CurrentFrameRate); + string startTime = string.Format(timeFormat, p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, (int)Math.Round(p.StartTime.Milliseconds / factor)); + string endTime = string.Format(timeFormat, p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, (int)Math.Round(p.EndTime.Milliseconds / factor)); + sb.AppendFormat(paragraphWriteFormat, startTime, endTime, DvdStudioPro.EncodeStyles(p.Text)); + } + return sb.ToString().Trim(); + } + + public static byte GetFrameFromMilliseconds(int milliseconds, double frameRate) + { + return (byte)Math.Round(milliseconds / (TimeCode.BaseUnit / frameRate)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + int number = 0; + foreach (string line in lines) + { + if (!string.IsNullOrWhiteSpace(line) && line[0] != '$' && !line.StartsWith("//")) + { + if (RegexTimeCodes.Match(line).Success) + { + string[] toPart = line.Substring(0, 24).Trim(',').Split(','); + var p = new Paragraph(); + if (toPart.Length == 2 && + DvdStudioPro.GetTimeCode(p.StartTime, toPart[0]) && + DvdStudioPro.GetTimeCode(p.EndTime, toPart[1])) + { + number++; + p.Number = number; + string text = line.Substring(25).Trim(); + p.Text = text.Replace(" | ", Environment.NewLine).Replace("|", Environment.NewLine); + p.Text = DvdStudioPro.DecodeStyles(p.Text); + subtitle.Paragraphs.Add(p); + } + } + else + { + _errorCount++; + } + } + } + } + + } +} diff --git a/libse/SubtitleFormats/DvdSubtitle.cs b/libse/SubtitleFormats/DvdSubtitle.cs new file mode 100644 index 000000000..1526f022c --- /dev/null +++ b/libse/SubtitleFormats/DvdSubtitle.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class DvdSubtitle : SubtitleFormat + { + + private static readonly Regex RegexTimeCodes = new Regex(@"^\{T\ \d+:\d+:\d+:\d+$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".sub"; } + } + + public override string Name + { + get { return "DVDSubtitle"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + Subtitle subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string paragraphWriteFormat = "T {0}\r\n{1}\r\n"; + const string timeFormat = "{0:00}:{1:00}:{2:00}:{3:00}"; + const string header = @"{HEAD +DISCID= +DVDTITLE= +CODEPAGE=1250 +FORMAT=ASCII +LANG= +TITLE=1 +ORIGINAL=ORIGINAL +AUTHOR= +WEB= +INFO= +LICENSE= +}"; + + StringBuilder sb = new StringBuilder(); + sb.AppendLine(header); + foreach (Paragraph p in subtitle.Paragraphs) + { + int milliseconds = p.StartTime.Milliseconds / 10; + string time = string.Format(timeFormat, p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, milliseconds); + sb.AppendLine("{" + string.Format(paragraphWriteFormat, time, p.Text) + "}"); + + milliseconds = p.EndTime.Milliseconds / 10; + time = string.Format(timeFormat, p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, milliseconds); + sb.AppendLine("{" + string.Format(paragraphWriteFormat, time, string.Empty) + "}"); + } + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + //{T 00:03:14:27 + //Some text + //} + _errorCount = 0; + bool textOn = false; + string text = string.Empty; + TimeCode start = new TimeCode(0); + TimeCode end = new TimeCode(0); + foreach (string line in lines) + { + if (textOn) + { + if (line.Trim() == "}") + { + Paragraph p = new Paragraph(); + p.Text = text; + p.StartTime = new TimeCode(start.TotalMilliseconds); + p.EndTime = new TimeCode(end.TotalMilliseconds); + + subtitle.Paragraphs.Add(p); + + text = string.Empty; + start = new TimeCode(0); + end = new TimeCode(0); + textOn = false; + } + else + { + if (text.Length == 0) + text = line; + else + text += Environment.NewLine + line; + } + } + else + { + if (RegexTimeCodes.Match(line).Success) + { + try + { + textOn = true; + string[] arr = line.Substring(3).Trim().Split(':'); + if (arr.Length == 4) + { + int hours = int.Parse(arr[0]); + int minutes = int.Parse(arr[1]); + int seconds = int.Parse(arr[2]); + int milliseconds = int.Parse(arr[3]); + if (arr[3].Length == 2) + milliseconds *= 10; + start = new TimeCode(hours, minutes, seconds, milliseconds); + } + } + catch + { + textOn = false; + _errorCount++; + } + } + } + } + + int index = 1; + foreach (Paragraph p in subtitle.Paragraphs) + { + Paragraph next = subtitle.GetParagraphOrDefault(index); + if (next != null) + { + p.EndTime.TotalMilliseconds = next.StartTime.TotalMilliseconds - 1; + } + index++; + } + + subtitle.RemoveEmptyLines(); + + subtitle.Renumber(); + } + } +} diff --git a/libse/SubtitleFormats/DvdSubtitleSystem.cs b/libse/SubtitleFormats/DvdSubtitleSystem.cs new file mode 100644 index 000000000..2cbcd609d --- /dev/null +++ b/libse/SubtitleFormats/DvdSubtitleSystem.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class DvdSubtitleSystem : SubtitleFormat + { + private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d:\d\d:\d\d:\d\d \d\d:\d\d:\d\d:\d\d ", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "DVD Subtitle System"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + + var sb = new StringBuilder(); + foreach (string line in lines) + sb.AppendLine(line); + if (sb.ToString().Contains("#INPOINT OUTPOINT PATH")) + return false; // Pinnacle Impression + + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + //00:03:15:22 00:03:23:10 This is line one. + //This is line two. + sb.AppendLine(string.Format("{0} {1} {2}", EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), HtmlUtil.RemoveHtmlTags(p.Text.Replace(Environment.NewLine, "//"), true))); + } + return sb.ToString(); + } + + private static string EncodeTimeCode(TimeCode time) + { + //00:03:15:22 (last is ms div 10) + return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", time.Hours, time.Minutes, time.Seconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + //00:03:15:22 00:03:23:10 This is line one. + //This is line two. + subtitle.Paragraphs.Clear(); + _errorCount = 0; + foreach (string line in lines) + { + if (RegexTimeCodes.IsMatch(line)) + { + string temp = line.Substring(0, RegexTimeCodes.Match(line).Length); + string start = temp.Substring(0, 11); + string end = temp.Substring(12, 11); + + string[] startParts = start.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + string[] endParts = end.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + if (startParts.Length == 4 && endParts.Length == 4) + { + string text = line.Remove(0, RegexTimeCodes.Match(line).Length - 1).Trim(); + text = text.Replace("//", Environment.NewLine); + var p = new Paragraph(DecodeTimeCode(startParts), DecodeTimeCode(endParts), text); + subtitle.Paragraphs.Add(p); + } + } + else + { + _errorCount += 10; + } + } + + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + //00:00:07:12 + string hour = parts[0]; + string minutes = parts[1]; + string seconds = parts[2]; + string msDiv10 = parts[3]; + + return new TimeCode(int.Parse(hour), int.Parse(minutes), int.Parse(seconds), FramesToMillisecondsMax999(int.Parse(msDiv10))); + } + + } +} diff --git a/libse/SubtitleFormats/ELRStudioClosedCaption.cs b/libse/SubtitleFormats/ELRStudioClosedCaption.cs new file mode 100644 index 000000000..1699c9d70 --- /dev/null +++ b/libse/SubtitleFormats/ELRStudioClosedCaption.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class ELRStudioClosedCaption : SubtitleFormat + { + public override string Extension + { + get { return ".elr"; } + } + + public override string Name + { + get { return "ELRStudio Closed Caption"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public static void Save(string fileName) + { + using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write)) + { + //... + } + } + + public override bool IsMine(List lines, string fileName) + { + if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName)) + { + var fi = new FileInfo(fileName); + if (fi.Length >= 640 && fi.Length < 1024000) // not too small or too big + { + if (fileName.EndsWith(".elr", StringComparison.OrdinalIgnoreCase)) + { + byte[] buffer = FileUtil.ReadAllBytesShared(fileName); + byte[] compareBuffer = { 0x05, 0x01, 0x0D, 0x15, 0x11, 0x00, 0xA9, 0x00, 0x45, 0x00, 0x6C, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x6D, 0x00, 0x20, 0x00, 0x53, 0x00, 0x74, 0x00, 0x75, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6F, 0x00 }; + + for (int i = 6; i < compareBuffer.Length; i++) + if (buffer[i] != compareBuffer[i]) + return false; + + var sub = new Subtitle(); + LoadSubtitle(sub, lines, fileName); + return sub.Paragraphs.Count > 0; + } + } + } + return false; + } + + public override string ToText(Subtitle subtitle, string title) + { + return "Not supported!"; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + subtitle.Paragraphs.Clear(); + subtitle.Header = null; + byte[] buffer = FileUtil.ReadAllBytesShared(fileName); + + int i = 128; + while (i < buffer.Length - 40) + { + try + { + if (buffer[i] == 0xc4 && buffer[i + 1] == 9 && buffer[i + 2] == 0 && buffer[i + 3] == 0x10) // start time (hopefully) + { + var p = new Paragraph(); + p.StartTime = GetTimeCode(buffer, i + 4); + i += 7; + + // seek to endtime + while (i < buffer.Length - 10 && !(buffer[i] == 0xc4 && buffer[i + 1] == 9 && buffer[i + 2] == 0 && buffer[i + 3] == 0x10)) + { + i++; + } + if (buffer[i] == 0xc4 && buffer[i + 1] == 9 && buffer[i + 2] == 0 && buffer[i + 3] == 0x10) + { + p.EndTime = GetTimeCode(buffer, i + 4); + i += 7; + } + if (p.EndTime.TotalMilliseconds == 0) + { + p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + 2000; + } + + // seek to text + var sb = new StringBuilder(); + while (i < buffer.Length - 10 && !(buffer[i] == 0xc4 && buffer[i + 1] == 9 && buffer[i + 2] == 0 && buffer[i + 3] == 0x10)) + { + if (buffer[i] == 9 && buffer[i + 1] == 0 && buffer[i + 2] == 0x44) + { + var length = buffer[i - 1]; + i += 12; + for (int j = i; j < i + (length * 4); j += 4) + { + sb.Append(Encoding.GetEncoding(1252).GetString(buffer, j, 1)); + } + sb.AppendLine(); + } + else + { + i++; + } + } + p.Text = p.Text + " " + sb.ToString().TrimEnd(); + subtitle.Paragraphs.Add(p); + } + else + { + i++; + } + } + catch + { + i += 5; + } + } + subtitle.Renumber(); + } + + private static TimeCode GetTimeCode(byte[] buffer, int idx) + { + try + { + int frames = int.Parse(buffer[idx].ToString("X4")); + int seconds = int.Parse(buffer[idx + 1].ToString("X4")); + int minutes = int.Parse(buffer[idx + 2].ToString("X4")); + int hours = int.Parse(buffer[idx + 3].ToString("X4")); + return new TimeCode(hours, minutes, seconds, FramesToMillisecondsMax999(frames)); + } + catch + { + return new TimeCode(0, 0, 0, 0); + } + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/Ebu.cs b/libse/SubtitleFormats/Ebu.cs new file mode 100644 index 000000000..bc1b3dd90 --- /dev/null +++ b/libse/SubtitleFormats/Ebu.cs @@ -0,0 +1,1138 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + /// + /// EBU Subtitling data exchange format + /// + public class Ebu : SubtitleFormat + { + + public interface IEbuUiHelper + { + void Initialize(EbuGeneralSubtitleInformation header, byte justificationCode, string fileName, Subtitle subtitle); + bool ShowDialogOk(); + byte JustificationCode { get; set; } + } + + public static IEbuUiHelper EbuUiHelper { get; set; } + + private static readonly Regex RegExpr = new Regex(@"^[a-f0-9]{6}$", RegexOptions.Compiled); + + public List VerticalPositions = new List(); + public List JustificationCodes = new List(); + + public EbuGeneralSubtitleInformation Header; + + /// + /// GSI block (1024 bytes) + /// + public class EbuGeneralSubtitleInformation + { + public string CodePageNumber { get; set; } // 0..2 + public string DiskFormatCode { get; set; } // 3..10 + public string DisplayStandardCode { get; set; } // 11 + public string CharacterCodeTableNumber { get; set; } // 12..13 + public string LanguageCode { get; set; } // 14..15 + public string OriginalProgrammeTitle { get; set; } // 16..47 + public string OriginalEpisodeTitle { get; set; } + public string TranslatedProgrammeTitle { get; set; } + public string TranslatedEpisodeTitle { get; set; } + public string TranslatorsName { get; set; } + public string TranslatorsContactDetails { get; set; } + public string SubtitleListReferenceCode { get; set; } + public string CreationDate { get; set; } + public string RevisionDate { get; set; } + public string RevisionNumber { get; set; } + public string TotalNumberOfTextAndTimingInformationBlocks { get; set; } + public string TotalNumberOfSubtitles { get; set; } + public string TotalNumberOfSubtitleGroups { get; set; } + public string MaximumNumberOfDisplayableCharactersInAnyTextRow { get; set; } + public string MaximumNumberOfDisplayableRows { get; set; } + public string TimeCodeStatus { get; set; } + public string TimeCodeStartOfProgramme { get; set; } + public string TimeCodeFirstInCue { get; set; } + public string TotalNumberOfDisks { get; set; } + public string DiskSequenceNumber { get; set; } + public string CountryOfOrigin { get; set; } + public string Publisher { get; set; } + public string EditorsName { get; set; } + public string EditorsContactDetails { get; set; } + public string SpareBytes { get; set; } + public string UserDefinedArea { get; set; } + + public double FrameRate + { + get + { + if (DiskFormatCode.StartsWith("STL23", StringComparison.Ordinal)) + return 23.0; + if (DiskFormatCode.StartsWith("STL24", StringComparison.Ordinal)) + return 24.0; + if (DiskFormatCode.StartsWith("STL25", StringComparison.Ordinal)) + return 25.0; + if (DiskFormatCode.StartsWith("STL29", StringComparison.Ordinal)) + return 29.0; + if (DiskFormatCode.StartsWith("STL35", StringComparison.Ordinal)) + return 35.0; + if (DiskFormatCode.StartsWith("STL48", StringComparison.Ordinal)) + return 48.0; + if (DiskFormatCode.StartsWith("STL50", StringComparison.Ordinal)) + return 50.0; + if (DiskFormatCode.StartsWith("STL60", StringComparison.Ordinal)) + return 60.0; + return 30.0; // should be DiskFormatcode STL30.01 + } + } + + public EbuGeneralSubtitleInformation() + { + CodePageNumber = "437"; + DiskFormatCode = "STL25.01"; + DisplayStandardCode = "0"; + CharacterCodeTableNumber = "00"; + LanguageCode = "0A"; + OriginalProgrammeTitle = "No Title "; + OriginalEpisodeTitle = " "; + TranslatedProgrammeTitle = string.Empty.PadLeft(32, ' '); + TranslatedEpisodeTitle = string.Empty.PadLeft(32, ' '); + TranslatorsName = string.Empty.PadLeft(32, ' '); + TranslatorsContactDetails = string.Empty.PadLeft(32, ' '); + SubtitleListReferenceCode = "0 "; + CreationDate = "101021"; + RevisionDate = "101021"; + RevisionNumber = "01"; + TotalNumberOfTextAndTimingInformationBlocks = "00725"; + TotalNumberOfSubtitles = "00725"; + TotalNumberOfSubtitleGroups = "001"; + MaximumNumberOfDisplayableCharactersInAnyTextRow = "40"; + MaximumNumberOfDisplayableRows = "23"; + TimeCodeStatus = "1"; + TimeCodeStartOfProgramme = "00000000"; + TimeCodeFirstInCue = "00000001"; + TotalNumberOfDisks = "1"; + DiskSequenceNumber = "1"; + CountryOfOrigin = "USA"; + Publisher = string.Empty.PadLeft(32, ' '); + EditorsName = string.Empty.PadLeft(32, ' '); + EditorsContactDetails = string.Empty.PadLeft(32, ' '); + SpareBytes = string.Empty.PadLeft(75, ' '); + UserDefinedArea = string.Empty.PadLeft(576, ' '); + } + + public override string ToString() + { + string result = + CodePageNumber + + DiskFormatCode + + DisplayStandardCode + + CharacterCodeTableNumber + + LanguageCode + + OriginalProgrammeTitle + + OriginalEpisodeTitle + + TranslatedProgrammeTitle + + TranslatedEpisodeTitle + + TranslatorsName + + TranslatorsContactDetails + + SubtitleListReferenceCode + + CreationDate + + RevisionDate + + RevisionNumber + + TotalNumberOfTextAndTimingInformationBlocks + + TotalNumberOfSubtitles + + TotalNumberOfSubtitleGroups + + MaximumNumberOfDisplayableCharactersInAnyTextRow + + MaximumNumberOfDisplayableRows + + TimeCodeStatus + + TimeCodeStartOfProgramme + + TimeCodeFirstInCue + + TotalNumberOfDisks + + DiskSequenceNumber + + CountryOfOrigin + + Publisher + + EditorsName + + EditorsContactDetails + + SpareBytes + + UserDefinedArea; + if (result.Length == 1024) + return result; + return "Length must be 1024 but is " + result.Length; + } + } + + /// + /// TTI block 128 bytes + /// + private class EbuTextTimingInformation + { + public byte SubtitleGroupNumber { get; set; } + public UInt16 SubtitleNumber { get; set; } + public byte ExtensionBlockNumber { get; set; } + public byte CumulativeStatus { get; set; } + public int TimeCodeInHours { get; set; } + public int TimeCodeInMinutes { get; set; } + public int TimeCodeInSeconds { get; set; } + public int TimeCodeInMilliseconds { get; set; } + public int TimeCodeOutHours { get; set; } + public int TimeCodeOutMinutes { get; set; } + public int TimeCodeOutSeconds { get; set; } + public int TimeCodeOutMilliseconds { get; set; } + public byte VerticalPosition { get; set; } + public byte JustificationCode { get; set; } + public byte CommentFlag { get; set; } + public string TextField { get; set; } + + public EbuTextTimingInformation() + { + SubtitleGroupNumber = 0; + ExtensionBlockNumber = 255; + CumulativeStatus = 0; + VerticalPosition = 0x16; + JustificationCode = 2; + CommentFlag = 0; + } + + public byte[] GetBytes(EbuGeneralSubtitleInformation header) + { + byte[] buffer = new byte[128]; // Text and Timing Information (TTI) block consists of 128 bytes + + buffer[0] = SubtitleGroupNumber; + byte[] temp = BitConverter.GetBytes(SubtitleNumber); + buffer[1] = temp[0]; + buffer[2] = temp[1]; + buffer[3] = ExtensionBlockNumber; + buffer[4] = CumulativeStatus; + + buffer[5] = (byte)TimeCodeInHours; + buffer[6] = (byte)TimeCodeInMinutes; + buffer[7] = (byte)TimeCodeInSeconds; + buffer[8] = GetFrameFromMilliseconds(TimeCodeInMilliseconds, header.FrameRate); + + buffer[9] = (byte)TimeCodeOutHours; + buffer[10] = (byte)TimeCodeOutMinutes; + buffer[11] = (byte)TimeCodeOutSeconds; + buffer[12] = GetFrameFromMilliseconds(TimeCodeOutMilliseconds, header.FrameRate); + + buffer[13] = VerticalPosition; + buffer[14] = JustificationCode; + buffer[15] = CommentFlag; + + Encoding encoding = Encoding.Default; + if (header.CharacterCodeTableNumber == "00") + { + encoding = Encoding.GetEncoding(20269); + // 0xC1—0xCF combines characters - http://en.wikipedia.org/wiki/ISO/IEC_6937 + TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc1 }), "ÀÈÌÒÙàèìòù", "AEIOUaeiou"); + TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc2 }), "ÁĆÉÍĹŃÓŔŚÚÝŹáćéģíĺńóŕśúýź", "ACEILNORSUYZacegilnorsuyz"); + TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc3 }), "ÂĈÊĜĤÎĴÔŜÛŴŶâĉêĝĥîĵôŝûŵŷ", "ACEGHIJOSUWYaceghijosuwy"); + TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc4 }), "ÃĨÑÕŨãĩñõũ", "AINOUainou"); + TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc5 }), "ĀĒĪŌŪāēīōū", "AEIOUaeiou"); + TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc6 }), "ĂĞŬăğŭ", "AGUagu"); + TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc7 }), "ĊĖĠİŻċėġıż", "CEGIZcegiz"); + TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc8 }), "ÄËÏÖÜŸäëïöüÿ", "AEIOUYaeiouy"); + TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xca }), "ÅŮåů", "AUau"); + TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xcb }), "ÇĢĶĻŅŖŞŢçķļņŗşţ", "CGKLNRSTcklnrst"); + TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xcd }), "ŐŰőű", "OUou"); + TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xce }), "ĄĘĮŲąęįų", "AEIUaeiu"); + TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xcf }), "ČĎĚĽŇŘŠŤŽčďěľňřšťž", "CDELNRSTZcdelnrstz"); + } + else if (header.CharacterCodeTableNumber == "01") // Latin/Cyrillic alphabet - from ISO 8859/5-1988 + { + encoding = Encoding.GetEncoding("ISO-8859-5"); + } + else if (header.CharacterCodeTableNumber == "02") // Latin/Arabic alphabet - from ISO 8859/6-1987 + { + encoding = Encoding.GetEncoding("ISO-8859-6"); + } + else if (header.CharacterCodeTableNumber == "03") // Latin/Greek alphabet - from ISO 8859/7-1987 + { + encoding = Encoding.GetEncoding("ISO-8859-7"); // or ISO-8859-1 ? + } + else if (header.CharacterCodeTableNumber == "04") // Latin/Hebrew alphabet - from ISO 8859/8-1988 + { + encoding = Encoding.GetEncoding("ISO-8859-8"); + } + + // italic/underline + string italicsOn = encoding.GetString(new byte[] { 0x80 }); + string italicsOff = encoding.GetString(new byte[] { 0x81 }); + string underlineOn = encoding.GetString(new byte[] { 0x82 }); + string underlineOff = encoding.GetString(new byte[] { 0x83 }); + if (Utilities.CountTagInText(TextField, "") == 1 && TextField.StartsWith("") && TextField.EndsWith("")) // italic on all lines + TextField = TextField.Replace(Environment.NewLine, Environment.NewLine + ""); + TextField = TextField.Replace("", italicsOn); + TextField = TextField.Replace("", italicsOn); + TextField = TextField.Replace("", italicsOff); + TextField = TextField.Replace("", italicsOff); + TextField = TextField.Replace("", underlineOn); + TextField = TextField.Replace("", underlineOn); + TextField = TextField.Replace("", underlineOff); + TextField = TextField.Replace("", underlineOff); + + //em-dash (–) tags + // TextField = TextField.Replace("–", "Ð"); + + //font tags + if (header.DisplayStandardCode == "0") // Open subtitling + { + TextField = HtmlUtil.RemoveHtmlTags(TextField, true); + } + else // teletext + { + var lines = TextField.SplitToLines(); + var sb = new StringBuilder(); + string veryFirstColor = null; + foreach (string line in lines) + { + string firstColor = null; + string s = line; + var start = s.IndexOf("= 0) + { + int end = s.IndexOf('>', start); + if (end > 0) + { + string f = s.Substring(start, end - start); + if (f.Contains(" color=")) + { + var colorStart = f.IndexOf(" color=", StringComparison.Ordinal); + if (s.IndexOf('"', colorStart + " color=".Length + 1) > 0) + { + int colorEnd = f.IndexOf('"', colorStart + " color=".Length + 1); + if (colorStart > 1) + { + string color = f.Substring(colorStart + 7, colorEnd - (colorStart + 7)); + color = color.Trim('\''); + color = color.Trim('\"'); + color = color.Trim('#'); + + s = s.Remove(start, end - start + 1); + if (veryFirstColor == null) + veryFirstColor = GetNearestEbuColorCode(color, encoding); + firstColor = GetNearestEbuColorCode(color, encoding); + } + } + } + } + } + //byte colorByte = 0x07; // white + byte colorByte = 255; + if (!string.IsNullOrEmpty(veryFirstColor)) + colorByte = encoding.GetBytes(veryFirstColor)[0]; + if (!string.IsNullOrEmpty(firstColor)) + colorByte = encoding.GetBytes(firstColor)[0]; + string prefix = encoding.GetString(new byte[] { 0xd, colorByte, 0xb, 0xb }); + + if (colorByte != 255) + sb.Append(prefix); + sb.AppendLine(s); + } + TextField = HtmlUtil.RemoveHtmlTags(sb.ToString()).TrimEnd(); + } + + // newline + string newline = encoding.GetString(new byte[] { 0x0a, 0x0a, 0x8a, 0x8a }); + + if (header.DisplayStandardCode == "0") + newline = encoding.GetString(new byte[] { 0x8A }); //8Ah=CR/LF + // newline = encoding.GetString(new byte[] { 0x85, 0x8A, 0x0D, 0x84, 0x80 }); //85h=boxing off, 8Ah=CR/LF, 84h=boxing on, 80h, Italics on + + TextField = TextField.Replace(Environment.NewLine, newline); + + string endOfLine = encoding.GetString(new byte[] { 0x0a, 0x0a, 0x8a }); + + if (header.DisplayStandardCode == "0") + { + endOfLine = string.Empty; + } + TextField += endOfLine; + + // save em-dash indexes (–) + var indexOfEmdash = new List(); + for (int j = 0; j < TextField.Length; j++) + { + if (TextField[j] == '–') + indexOfEmdash.Add(j); + } + + // convert text to bytes + byte[] bytes = encoding.GetBytes(TextField); + + // restore em-dashes (–) + foreach (int index in indexOfEmdash) + { + bytes[index] = 0xd0; + } + + for (int i = 0; i < 112; i++) + { + if (i < bytes.Length) + buffer[16 + i] = bytes[i]; + //else if (i == bytes.Length) + // buffer[16 + i] = 0x8f; + else + buffer[16 + i] = 0x8f; + } + return buffer; + } + + private static string GetNearestEbuColorCode(string color, Encoding encoding) + { + color = color.ToLower(); + if (color == "black" || color == "000000") + return encoding.GetString(new byte[] { 0x00 }); // black + if (color == "red" || color == "ff0000") + return encoding.GetString(new byte[] { 0x01 }); // red + if (color == "green" || color == "00ff00") + return encoding.GetString(new byte[] { 0x02 }); // green + if (color == "yellow" || color == "ffff00") + return encoding.GetString(new byte[] { 0x03 }); // yellow + if (color == "blue" || color == "0000ff") + return encoding.GetString(new byte[] { 0x04 }); // blue + if (color == "magenta" || color == "ff00ff") + return encoding.GetString(new byte[] { 0x05 }); // magenta + if (color == "cyan" || color == "00ffff") + return encoding.GetString(new byte[] { 0x06 }); // cyan + if (color == "white" || color == "ffffff") + return encoding.GetString(new byte[] { 0x07 }); // white + if (color.Length == 6) + { + if (RegExpr.IsMatch(color)) + { + const int maxDiff = 130; + int r = int.Parse(color.Substring(0, 2), System.Globalization.NumberStyles.HexNumber); + int g = int.Parse(color.Substring(2, 2), System.Globalization.NumberStyles.HexNumber); + int b = int.Parse(color.Substring(4, 2), System.Globalization.NumberStyles.HexNumber); + if (r < maxDiff && g < maxDiff && b < maxDiff) + return encoding.GetString(new byte[] { 0x00 }); // black + if (r > 255 - maxDiff && g < maxDiff && b < maxDiff) + return encoding.GetString(new byte[] { 0x01 }); // red + if (r < maxDiff && g > 255 - maxDiff && b < maxDiff) + return encoding.GetString(new byte[] { 0x02 }); // green + if (r > 255 - maxDiff && g > 255 - maxDiff && b < maxDiff) + return encoding.GetString(new byte[] { 0x03 }); // yellow + if (r < maxDiff && g < maxDiff && b > 255 - maxDiff) + return encoding.GetString(new byte[] { 0x04 }); // blue + if (r > 255 - maxDiff && g < maxDiff && b > 255 - maxDiff) + return encoding.GetString(new byte[] { 0x05 }); // magenta + if (r < maxDiff && g > 255 - maxDiff && b > 255 - maxDiff) + return encoding.GetString(new byte[] { 0x06 }); // cyan + if (r > 255 - maxDiff && g > 255 - maxDiff && b > 255 - maxDiff) + return encoding.GetString(new byte[] { 0x07 }); // white + } + } + return string.Empty; + } + + private static string ReplaceSpecialCharactersWithTwoByteEncoding(string text, string specialCharacter, string originalCharacters, string newCharacters) + { + if (originalCharacters.Length != newCharacters.Length) + throw new ArgumentException("originalCharacters and newCharacters must have equal length"); + + for (int i = 0; i < newCharacters.Length; i++) + text = text.Replace(originalCharacters[i].ToString(), specialCharacter + newCharacters[i]); + return text; + } + + public static byte GetFrameFromMilliseconds(int milliseconds, double frameRate) + { + int frame = (int)(milliseconds / (TimeCode.BaseUnit / frameRate)); + return (byte)(frame); + } + } + + public override string Extension + { + get { return ".stl"; } + } + + public const string NameOfFormat = "EBU STL"; + + public override string Name + { + get { return NameOfFormat; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public static void Save(string fileName, Subtitle subtitle) + { + Save(fileName, subtitle, false); + } + + public static void Save(string fileName, Subtitle subtitle, bool batchMode) + { + var header = new EbuGeneralSubtitleInformation(); + if (EbuUiHelper == null) + return; + + if (subtitle.Header != null && subtitle.Header.Length > 1024 && (subtitle.Header.Contains("STL24") || subtitle.Header.Contains("STL25") || subtitle.Header.Contains("STL29") || subtitle.Header.Contains("STL30"))) + { + header = ReadHeader(Encoding.UTF8.GetBytes(subtitle.Header)); + EbuUiHelper.Initialize(header, 0, null, subtitle); + } + else + { + EbuUiHelper.Initialize(header, 0, fileName, subtitle); + } + + if (!batchMode && !EbuUiHelper.ShowDialogOk()) + return; + + using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write)) + { + header.TotalNumberOfSubtitles = subtitle.Paragraphs.Count.ToString("D5"); // seems to be 1 higher than actual number of subtitles + header.TotalNumberOfTextAndTimingInformationBlocks = header.TotalNumberOfSubtitles; + + var today = string.Format("{0:yyMMdd}", DateTime.Now); + if (today.Length == 6) + { + header.CreationDate = today; + header.RevisionDate = today; + } + + Paragraph firstParagraph = subtitle.GetParagraphOrDefault(0); + if (firstParagraph != null) + { + TimeCode tc = firstParagraph.StartTime; + string firstTimeCode = string.Format("{0:00}{1:00}{2:00}{3:00}", tc.Hours, tc.Minutes, tc.Seconds, EbuTextTimingInformation.GetFrameFromMilliseconds(tc.Milliseconds, header.FrameRate)); + if (firstTimeCode.Length == 8) + header.TimeCodeFirstInCue = firstTimeCode; + } + + byte[] buffer = Encoding.Default.GetBytes(header.ToString()); + fs.Write(buffer, 0, buffer.Length); + + int subtitleNumber = 0; + foreach (Paragraph p in subtitle.Paragraphs) + { + var tti = new EbuTextTimingInformation(); + + int rows; + if (!int.TryParse(header.MaximumNumberOfDisplayableRows, out rows)) + rows = 23; + + if (header.DisplayStandardCode == "1" || header.DisplayStandardCode == "2") // teletext + rows = 23; + else if (header.DisplayStandardCode == "0" && header.MaximumNumberOfDisplayableRows == "02") // open subtitling + rows = 15; + + if (p.Text.StartsWith("{\\an7}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an8}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an9}", StringComparison.Ordinal)) + { + tti.VerticalPosition = 1; // top (vertical) + } + else if (p.Text.StartsWith("{\\an4}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an5}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an6}", StringComparison.Ordinal)) + { + tti.VerticalPosition = (byte)(rows / 2); // middle (vertical) + } + else + { + int startRow = (rows - 1) - Utilities.CountTagInText(p.Text, Environment.NewLine) * 2; + if (startRow < 0) + startRow = 0; + tti.VerticalPosition = (byte)startRow; // bottom (vertical) + } + + tti.JustificationCode = EbuUiHelper.JustificationCode; + if (p.Text.StartsWith("{\\an1}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an4}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an7}", StringComparison.Ordinal)) + { + tti.JustificationCode = 1; // 01h=left-justified text + } + else if (p.Text.StartsWith("{\\an3}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an6}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an9}", StringComparison.Ordinal)) + { + tti.JustificationCode = 3; // 03h=right-justified + } + else // If it's not left- or right-justified, it's centred. + { + tti.JustificationCode = 2; // 02h=centred text + } + + tti.SubtitleNumber = (ushort)subtitleNumber; + tti.TextField = p.Text; + int startTag = tti.TextField.IndexOf('}'); + if (tti.TextField.StartsWith("{\\", StringComparison.Ordinal) && startTag > 0 && startTag < 10) + { + tti.TextField = tti.TextField.Remove(0, startTag + 1); + } + + tti.TimeCodeInHours = p.StartTime.Hours; + tti.TimeCodeInMinutes = p.StartTime.Minutes; + tti.TimeCodeInSeconds = p.StartTime.Seconds; + tti.TimeCodeInMilliseconds = p.StartTime.Milliseconds; + tti.TimeCodeOutHours = p.EndTime.Hours; + tti.TimeCodeOutMinutes = p.EndTime.Minutes; + tti.TimeCodeOutSeconds = p.EndTime.Seconds; + tti.TimeCodeOutMilliseconds = p.EndTime.Milliseconds; + buffer = tti.GetBytes(header); + fs.Write(buffer, 0, buffer.Length); + subtitleNumber++; + } + } + } + + public override bool IsMine(List lines, string fileName) + { + if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName)) + { + var fi = new FileInfo(fileName); + if (fi.Length >= 1024 + 128 && fi.Length < 1024000) // not too small or too big + { + try + { + byte[] buffer = FileUtil.ReadAllBytesShared(fileName); + EbuGeneralSubtitleInformation header = ReadHeader(buffer); + if (header.DiskFormatCode.StartsWith("STL23", StringComparison.Ordinal) || + header.DiskFormatCode.StartsWith("STL24", StringComparison.Ordinal) || + header.DiskFormatCode.StartsWith("STL25", StringComparison.Ordinal) || + header.DiskFormatCode.StartsWith("STL29", StringComparison.Ordinal) || + header.DiskFormatCode.StartsWith("STL30", StringComparison.Ordinal) || + header.DiskFormatCode.StartsWith("STL35", StringComparison.Ordinal) || + header.DiskFormatCode.StartsWith("STL48", StringComparison.Ordinal) || + header.DiskFormatCode.StartsWith("STL50", StringComparison.Ordinal) || + header.DiskFormatCode.StartsWith("STL60", StringComparison.Ordinal)) + { + return Utilities.IsInteger(header.CodePageNumber) || fileName.EndsWith("stl", StringComparison.OrdinalIgnoreCase); + } + } + catch + { + return false; + } + } + } + return false; + } + + public override string ToText(Subtitle subtitle, string title) + { + return "Not supported!"; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + subtitle.Paragraphs.Clear(); + subtitle.Header = null; + byte[] buffer = FileUtil.ReadAllBytesShared(fileName); + EbuGeneralSubtitleInformation header = ReadHeader(buffer); + subtitle.Header = Encoding.UTF8.GetString(buffer); + Paragraph last = null; + byte lastExtensionBlockNumber = 0xff; + JustificationCodes = new List(); + VerticalPositions = new List(); + foreach (EbuTextTimingInformation tti in ReadTTI(buffer, header)) + { + if (tti.ExtensionBlockNumber != 0xfe) // FEh : Reserved for User Data + { + var p = new Paragraph(); + p.Text = tti.TextField; + p.StartTime = new TimeCode(tti.TimeCodeInHours, tti.TimeCodeInMinutes, tti.TimeCodeInSeconds, tti.TimeCodeInMilliseconds); + p.EndTime = new TimeCode(tti.TimeCodeOutHours, tti.TimeCodeOutMinutes, tti.TimeCodeOutSeconds, tti.TimeCodeOutMilliseconds); + + if (lastExtensionBlockNumber != 0xff && last != null) + { + last.Text += p.Text; // merge text + } + else + { + subtitle.Paragraphs.Add(p); + last = p; + } + lastExtensionBlockNumber = tti.ExtensionBlockNumber; + } + } + subtitle.Renumber(); + Header = header; + } + + public static EbuGeneralSubtitleInformation ReadHeader(byte[] buffer) + { + var header = new EbuGeneralSubtitleInformation(); + header.CodePageNumber = Encoding.Default.GetString(buffer, 0, 3); + header.DiskFormatCode = Encoding.Default.GetString(buffer, 3, 8); + header.DisplayStandardCode = Encoding.Default.GetString(buffer, 11, 1); + header.CharacterCodeTableNumber = Encoding.Default.GetString(buffer, 12, 2); + header.LanguageCode = Encoding.Default.GetString(buffer, 14, 2); + header.OriginalProgrammeTitle = Encoding.Default.GetString(buffer, 16, 32); + header.OriginalEpisodeTitle = Encoding.Default.GetString(buffer, 48, 32); + header.TranslatedProgrammeTitle = Encoding.Default.GetString(buffer, 80, 32); + header.TranslatedEpisodeTitle = Encoding.Default.GetString(buffer, 112, 32); + header.TranslatorsName = Encoding.Default.GetString(buffer, 144, 32); + header.TranslatorsContactDetails = Encoding.Default.GetString(buffer, 176, 32); + header.SubtitleListReferenceCode = Encoding.Default.GetString(buffer, 208, 16); + header.CreationDate = Encoding.Default.GetString(buffer, 224, 6); + header.RevisionDate = Encoding.Default.GetString(buffer, 230, 6); + header.RevisionNumber = Encoding.Default.GetString(buffer, 236, 2); + header.TotalNumberOfTextAndTimingInformationBlocks = Encoding.Default.GetString(buffer, 238, 5); + header.TotalNumberOfSubtitles = Encoding.Default.GetString(buffer, 243, 5); + header.TotalNumberOfSubtitleGroups = Encoding.Default.GetString(buffer, 248, 3); + header.MaximumNumberOfDisplayableCharactersInAnyTextRow = Encoding.Default.GetString(buffer, 251, 2); + header.MaximumNumberOfDisplayableRows = Encoding.Default.GetString(buffer, 253, 2); + header.TimeCodeStatus = Encoding.Default.GetString(buffer, 255, 1); + header.TimeCodeStartOfProgramme = Encoding.Default.GetString(buffer, 256, 8); + header.CountryOfOrigin = Encoding.Default.GetString(buffer, 274, 3); + header.SpareBytes = Encoding.Default.GetString(buffer, 373, 75); + header.UserDefinedArea = Encoding.Default.GetString(buffer, 448, 576); + + return header; + } + + /// + /// Get text with regard code page from header + /// + /// Skip next character + /// EBU header + /// data buffer + /// index to current byte in buffer + /// Character at index + private static string GetCharacter(out bool skipNext, EbuGeneralSubtitleInformation header, byte[] buffer, int index) + { + skipNext = false; + if (header.CharacterCodeTableNumber == "00") + { + //note that 0xC1—0xCF combines characters - http://en.wikipedia.org/wiki/ISO/IEC_6937 + var encoding = Encoding.GetEncoding(20269); + string next = encoding.GetString(buffer, index + 1, 1); + switch (buffer[index]) + { + case 0xc1: // Grave + skipNext = @"AEIOUaeiou".Contains(next); + switch (next) + { + case "A": return "À"; + case "E": return "È"; + case "I": return "Ì"; + case "O": return "Ò"; + case "U": return "Ù"; + case "a": return "à"; + case "e": return "è"; + case "i": return "ì"; + case "o": return "ò"; + case "u": return "ù"; + } + return string.Empty; + case 0xc2: // Acute + skipNext = @"ACEILNORSUYZacegilnorsuyz".Contains(next); + switch (next) + { + case "A": return "Á"; + case "C": return "Ć"; + case "E": return "É"; + case "I": return "Í"; + case "L": return "Ĺ"; + case "N": return "Ń"; + case "O": return "Ó"; + case "R": return "Ŕ"; + case "S": return "Ś"; + case "U": return "Ú"; + case "Y": return "Ý"; + case "Z": return "Ź"; + case "a": return "á"; + case "c": return "ć"; + case "e": return "é"; + case "g": return "ģ"; + case "i": return "í"; + case "l": return "ĺ"; + case "n": return "ń"; + case "o": return "ó"; + case "r": return "ŕ"; + case "s": return "ś"; + case "u": return "ú"; + case "y": return "ý"; + case "z": return "ź"; + } + return string.Empty; + case 0xc3: // Circumflex + skipNext = @"ACEGHIJOSUWYaceghijosuwy".Contains(next); + switch (next) + { + case "A": return "Â"; + case "C": return "Ĉ"; + case "E": return "Ê"; + case "G": return "Ĝ"; + case "H": return "Ĥ"; + case "I": return "Î"; + case "J": return "Ĵ"; + case "O": return "Ô"; + case "S": return "Ŝ"; + case "U": return "Û"; + case "W": return "Ŵ"; + case "Y": return "Ŷ"; + case "a": return "â"; + case "c": return "ĉ"; + case "e": return "ê"; + case "g": return "ĝ"; + case "h": return "ĥ"; + case "i": return "î"; + case "j": return "ĵ"; + case "o": return "ô"; + case "s": return "ŝ"; + case "u": return "û"; + case "w": return "ŵ"; + case "y": return "ŷ"; + } + return string.Empty; + case 0xc4: // Tilde + skipNext = @"AINOUainou".Contains(next); + switch (next) + { + case "A": return "Ã"; + case "I": return "Ĩ"; + case "N": return "Ñ"; + case "O": return "Õ"; + case "U": return "Ũ"; + case "a": return "ã"; + case "i": return "ĩ"; + case "n": return "ñ"; + case "o": return "õ"; + case "u": return "ũ"; + } + return string.Empty; + case 0xc5: // Macron + skipNext = @"AEIOUaeiou".Contains(next); + switch (next) + { + case "A": return "Ā"; + case "E": return "Ē"; + case "I": return "Ī"; + case "O": return "Ō"; + case "U": return "Ū"; + case "a": return "ā"; + case "e": return "ē"; + case "i": return "ī"; + case "o": return "ō"; + case "u": return "ū"; + } + return string.Empty; + case 0xc6: // Breve + skipNext = @"AGUagu".Contains(next); + switch (next) + { + case "A": return "Ă"; + case "G": return "Ğ"; + case "U": return "Ŭ"; + case "a": return "ă"; + case "g": return "ğ"; + case "u": return "ŭ"; + } + return string.Empty; + case 0xc7: // Dot + skipNext = @"CEGIZcegiz".Contains(next); + switch (next) + { + case "C": return "Ċ"; + case "E": return "Ė"; + case "G": return "Ġ"; + case "I": return "İ"; + case "Z": return "Ż"; + case "c": return "ċ"; + case "e": return "ė"; + case "g": return "ġ"; + case "i": return "ı"; + case "z": return "ż"; + } + return string.Empty; + case 0xc8: // Umlaut or diæresis + skipNext = @"AEIOUYaeiouy".Contains(next); + switch (next) + { + case "A": return "Ä"; + case "E": return "Ë"; + case "I": return "Ï"; + case "O": return "Ö"; + case "U": return "Ü"; + case "Y": return "Ÿ"; + case "a": return "ä"; + case "e": return "ë"; + case "i": return "ï"; + case "o": return "ö"; + case "u": return "ü"; + case "y": return "ÿ"; + } + return string.Empty; + case 0xca: // Ring + skipNext = @"AUau".Contains(next); + switch (next) + { + case "A": return "Å"; + case "U": return "Ů"; + case "a": return "å"; + case "u": return "ů"; + } + return string.Empty; + case 0xcb: // Cedilla + skipNext = @"CGKLNRSTcklnrst".Contains(next); + switch (next) + { + case "C": return "Ç"; + case "G": return "Ģ"; + case "K": return "Ķ"; + case "L": return "Ļ"; + case "N": return "Ņ"; + case "R": return "Ŗ"; + case "S": return "Ş"; + case "T": return "Ţ"; + case "c": return "ç"; + case "k": return "ķ"; + case "l": return "ļ"; + case "n": return "ņ"; + case "r": return "ŗ"; + case "s": return "ş"; + case "t": return "ţ"; + } + return string.Empty; + case 0xcd: // DoubleAcute + skipNext = @"OUou".Contains(next); + switch (next) + { + case "O": return "Ő"; + case "U": return "Ű"; + case "o": return "ő"; + case "u": return "ű"; + } + return string.Empty; + case 0xce: // Ogonek + skipNext = @"AEIUaeiu".Contains(next); + switch (next) + { + case "A": return "Ą"; + case "E": return "Ę"; + case "I": return "Į"; + case "U": return "Ų"; + case "a": return "ą"; + case "e": return "ę"; + case "i": return "į"; + case "u": return "ų"; + } + return string.Empty; + case 0xcf: // Caron + skipNext = @"CDELNRSTZcdelnrstz".Contains(next); + switch (next) + { + case "C": return "Č"; + case "D": return "Ď"; + case "E": return "Ě"; + case "L": return "Ľ"; + case "N": return "Ň"; + case "R": return "Ř"; + case "S": return "Š"; + case "T": return "Ť"; + case "Z": return "Ž"; + case "c": return "č"; + case "d": return "ď"; + case "e": return "ě"; + case "l": return "ľ"; + case "n": return "ň"; + case "r": return "ř"; + case "s": return "š"; + case "t": return "ť"; + case "z": return "ž"; + } + return string.Empty; + default: + return encoding.GetString(buffer, index, 1); + } + } + if (header.CharacterCodeTableNumber == "01") // Latin/Cyrillic alphabet - from ISO 8859/5-1988 + { + var encoding = Encoding.GetEncoding("ISO-8859-5"); + return encoding.GetString(buffer, index, 1); + } + if (header.CharacterCodeTableNumber == "02") // Latin/Arabic alphabet - from ISO 8859/6-1987 + { + var encoding = Encoding.GetEncoding("ISO-8859-6"); + return encoding.GetString(buffer, index, 1); + } + if (header.CharacterCodeTableNumber == "03") // Latin/Greek alphabet - from ISO 8859/7-1987 + { + var encoding = Encoding.GetEncoding("ISO-8859-7"); // or ISO-8859-1 ? + return encoding.GetString(buffer, index, 1); + } + if (header.CharacterCodeTableNumber == "04") // Latin/Hebrew alphabet - from ISO 8859/8-1988 + { + var encoding = Encoding.GetEncoding("ISO-8859-8"); + return encoding.GetString(buffer, index, 1); + } + + return string.Empty; + } + + private IEnumerable ReadTTI(byte[] buffer, EbuGeneralSubtitleInformation header) + { + const int startOfTTI = 1024; + const int ttiSize = 128; + const byte textFieldCRLF = 0x8A; + const byte textFieldTerminator = 0x8F; + const byte italicsOn = 0x80; + const byte italicsOff = 0x81; + const byte underlineOn = 0x82; + const byte underlineOff = 0x83; + + var list = new List(); + int index = startOfTTI; + while (index + ttiSize <= buffer.Length) + { + var tti = new EbuTextTimingInformation(); + + tti.SubtitleGroupNumber = buffer[index]; + tti.SubtitleNumber = (ushort)(buffer[index + 2] * 256 + buffer[index + 1]); + tti.ExtensionBlockNumber = buffer[index + 3]; + tti.CumulativeStatus = buffer[index + 4]; + + tti.TimeCodeInHours = buffer[index + 5 + 0]; + tti.TimeCodeInMinutes = buffer[index + 5 + 1]; + tti.TimeCodeInSeconds = buffer[index + 5 + 2]; + tti.TimeCodeInMilliseconds = (int)(TimeCode.BaseUnit / (header.FrameRate / buffer[index + 5 + 3])); + + tti.TimeCodeOutHours = buffer[index + 9 + 0]; + tti.TimeCodeOutMinutes = buffer[index + 9 + 1]; + tti.TimeCodeOutSeconds = buffer[index + 9 + 2]; + tti.TimeCodeOutMilliseconds = (int)(1000 / (header.FrameRate / buffer[index + 9 + 3])); + + tti.VerticalPosition = buffer[index + 13]; + VerticalPositions.Add(tti.VerticalPosition); + tti.JustificationCode = buffer[index + 14]; + JustificationCodes.Add(tti.JustificationCode); + tti.CommentFlag = buffer[index + 15]; + + // build text + bool skipNext = false; + var sb = new StringBuilder(); + string endTags = string.Empty; + string color = string.Empty; + string lastColor = string.Empty; + for (int i = 0; i < 112; i++) // skip fist byte (seems to be always 0xd/32/space - thx Iban) + { + byte b = buffer[index + 16 + i]; + if (b <= 0xf && (i == 0 || i == 2 || i == 3)) + { + // not used, 0=0xd, 2=0xb, 3=0xb + } + else if (skipNext) + { + skipNext = false; + } + else + { + if (b <= 0x17) + { + switch (b) + { + case 0x00: + case 0x10: + color = "Black"; + break; + case 0x01: + case 0x11: + color = "Red"; + break; + case 0x02: + case 0x12: + color = "Green"; + break; + case 0x03: + case 0x13: + color = "Yellow"; + break; + case 0x04: + case 0x14: + color = "Blue"; + break; + case 0x05: + case 0x15: + color = "Magenta"; + break; + case 0x06: + case 0x16: + color = "Cyan"; + break; + case 0x07: + case 0x17: + color = "White"; + break; + } + } + if (b == textFieldCRLF) + sb.AppendLine(); + else if (b == italicsOn) + sb.Append(""); + else if (b == italicsOff) + sb.Append(""); + else if (b == underlineOn) + sb.Append(""); + else if (b == underlineOff) + sb.Append(""); + //else if (b == 0xD0) // em-dash + // sb.Append('–'); + else if (b == textFieldTerminator) + break; + else if ((b >= 0x20 && b <= 0x7F) || b >= 0xA1) + { + string ch = GetCharacter(out skipNext, header, buffer, index + 16 + i); + if (ch != " ") + { + if (color != lastColor && color.Length > 0) + { + endTags = ""; + if (lastColor.Length > 0) + sb.Append(""); + sb.Append(""); + } + lastColor = color; + } + sb.Append(ch); + } + } + } + tti.TextField = sb.ToString().Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine).TrimEnd() + endTags; + + int rows; + if (!int.TryParse(header.MaximumNumberOfDisplayableRows, out rows)) + rows = 23; + if (tti.VerticalPosition < 3) + { + if (tti.JustificationCode == 1) // left + tti.TextField = "{\\an7}" + tti.TextField; + else if (tti.JustificationCode == 3) // right + tti.TextField = "{\\an9}" + tti.TextField; + else + tti.TextField = "{\\an8}" + tti.TextField; + } + else if (tti.VerticalPosition <= rows / 2 + 1) + { + if (tti.JustificationCode == 1) // left + tti.TextField = "{\\an4}" + tti.TextField; + else if (tti.JustificationCode == 3) // right + tti.TextField = "{\\an6}" + tti.TextField; + else + tti.TextField = "{\\an5}" + tti.TextField; + } + else + { + if (tti.JustificationCode == 1) // left + tti.TextField = "{\\an1}" + tti.TextField; + else if (tti.JustificationCode == 3) // right + tti.TextField = "{\\an3}" + tti.TextField; + } + index += ttiSize; + list.Add(tti); + } + return list; + } + + public override bool IsTextBased + { + get + { + return false; + } + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/Eeg708.cs b/libse/SubtitleFormats/Eeg708.cs new file mode 100644 index 000000000..9d6c819cd --- /dev/null +++ b/libse/SubtitleFormats/Eeg708.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class Eeg708 : SubtitleFormat + { + public override string Extension + { + get { return ".xml"; } + } + + public override string Name + { + get { return "EEG 708"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > 0; + } + + public override string ToText(Subtitle subtitle, string title) + { + string xmlStructure = + "" + Environment.NewLine + + ""; + + var xml = new XmlDocument(); + xml.LoadXml(xmlStructure); + + foreach (Paragraph p in subtitle.Paragraphs) + { + XmlNode paragraph = xml.CreateElement("Caption"); + XmlAttribute start = xml.CreateAttribute("timecode"); + start.InnerText = EncodeTimeCode(p.StartTime); + paragraph.Attributes.Append(start); + XmlNode text = xml.CreateElement("Text"); + text.InnerText = p.Text; + paragraph.AppendChild(text); + xml.DocumentElement.AppendChild(paragraph); + + paragraph = xml.CreateElement("Caption"); + start = xml.CreateAttribute("timecode"); + start.InnerText = EncodeTimeCode(p.EndTime); + paragraph.Attributes.Append(start); + xml.DocumentElement.AppendChild(paragraph); + } + + return ToUtf8XmlString(xml); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + + string allText = sb.ToString(); + if (!allText.Contains("", Environment.NewLine).Replace("
", Environment.NewLine); + TimeCode startTime = DecodeTimeCode(start.Split(':')); + lastParagraph = new Paragraph(s, startTime.TotalMilliseconds, startTime.TotalMilliseconds + 3000); + subtitle.Paragraphs.Add(lastParagraph); + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + _errorCount++; + } + } + subtitle.Renumber(); + } + + private static string EncodeTimeCode(TimeCode time) + { + return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", time.Hours, time.Minutes, time.Seconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + var hour = int.Parse(parts[0]); + var minutes = int.Parse(parts[1]); + var seconds = int.Parse(parts[2]); + var frames = int.Parse(parts[3]); + + return new TimeCode(hour, minutes, seconds, FramesToMillisecondsMax999(frames)); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/F4Rtf.cs b/libse/SubtitleFormats/F4Rtf.cs new file mode 100644 index 000000000..e45472884 --- /dev/null +++ b/libse/SubtitleFormats/F4Rtf.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class F4Rtf : F4Text + { + public override string Extension + { + get { return ".rtf"; } + } + + public override string Name + { + get { return "F4 Rich Text Format"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + if (fileName != null && !fileName.EndsWith(Extension, StringComparison.OrdinalIgnoreCase)) + return false; + + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + System.Windows.Forms.RichTextBox rtBox = new System.Windows.Forms.RichTextBox(); + rtBox.Text = ToF4Text(subtitle); + string rtf = rtBox.Rtf; + rtBox.Dispose(); + return rtf; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + var sb = new StringBuilder(); + foreach (string line in lines) + sb.AppendLine(line); + + string rtf = sb.ToString().Trim(); + if (!rtf.StartsWith("{\\rtf")) + return; + + var rtBox = new System.Windows.Forms.RichTextBox(); + try + { + rtBox.Rtf = rtf; + } + catch (Exception exception) + { + System.Diagnostics.Debug.WriteLine(exception.Message); + return; + } + string text = rtBox.Text; + rtBox.Dispose(); + LoadF4TextSubtitle(subtitle, text); + } + } +} diff --git a/libse/SubtitleFormats/F4Text.cs b/libse/SubtitleFormats/F4Text.cs new file mode 100644 index 000000000..a5aad5eef --- /dev/null +++ b/libse/SubtitleFormats/F4Text.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + /// + /// #00:00:06-8# + /// http://www.audiotranskription.de + /// + public class F4Text : SubtitleFormat + { + private static readonly Regex regexTimeCodes = new Regex(@"^\d\d:\d\d:\d\d-\d$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "F4 Text"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + if (fileName != null && !fileName.EndsWith(Extension, StringComparison.OrdinalIgnoreCase)) + return false; + + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public static string ToF4Text(Subtitle subtitle) + { + var sb = new StringBuilder(); + //double lastEndTimeMilliseconds = -1; + const string writeFormat = "{0}{1}"; + foreach (Paragraph p in subtitle.Paragraphs) + { + // if (p.StartTime.TotalMilliseconds == lastEndTimeMilliseconds) + sb.AppendFormat(writeFormat, HtmlUtil.RemoveHtmlTags(p.Text, true), EncodeTimeCode(p.EndTime)); + //else + // sb.Append(string.Format("{0}{1}{2}", EncodeTimeCode(p.StartTime), HtmlUtil.RemoveHtmlTags(p.Text), EncodeTimeCode(p.EndTime))); + //lastEndTimeMilliseconds = p.EndTime.TotalMilliseconds; + } + return sb.ToString().Trim(); + } + + public override string ToText(Subtitle subtitle, string title) + { + return ToF4Text(subtitle); + } + + private static string EncodeTimeCode(TimeCode time) + { + return string.Format(" #{0:00}:{1:00}:{2:00}-{3:0}# ", time.Hours, time.Minutes, time.Seconds, Math.Round(time.Milliseconds / 100.0, 0)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + var sb = new StringBuilder(); + foreach (string line in lines) + sb.AppendLine(line); + string text = sb.ToString(); + if (text.Contains("{\\rtf") || text.Contains("")) + return; + LoadF4TextSubtitle(subtitle, text); + } + + protected void LoadF4TextSubtitle(Subtitle subtitle, string text) + { + Paragraph p = null; + subtitle.Paragraphs.Clear(); + var arr = text.Trim().Split(new[] { '#' }, StringSplitOptions.RemoveEmptyEntries); + var currentText = new StringBuilder(); + foreach (string line in arr) + { + if (regexTimeCodes.IsMatch(line)) + { + if (p == null) + { + p = new Paragraph(); + if (currentText.Length > 0) + { + p.Text = currentText.ToString().Trim().Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine); + p.Text = p.Text.Trim('\n', '\r'); + subtitle.Paragraphs.Add(p); + p = new Paragraph(); + } + } + if (p.StartTime.TotalMilliseconds == 0 || currentText.Length == 0) + { + p.StartTime = DecodeTimeCode(line.Split(new[] { ':', '-' }, StringSplitOptions.RemoveEmptyEntries)); + } + else + { + p.EndTime = DecodeTimeCode(line.Split(new[] { ':', '-' }, StringSplitOptions.RemoveEmptyEntries)); + p.Text = currentText.ToString().Trim().Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine); + p.Text = p.Text.Trim('\n', '\r').Trim(); + subtitle.Paragraphs.Add(p); + p = null; + currentText.Clear(); + } + } + else + { + if (p == null && subtitle.Paragraphs.Count > 0) + { + p = new Paragraph(); + p.StartTime.TotalMilliseconds = subtitle.Paragraphs[subtitle.Paragraphs.Count - 1].EndTime.TotalMilliseconds; + } + currentText.AppendLine(line.Trim()); + } + } + if (currentText.Length > 0 && subtitle.Paragraphs.Count > 0 && currentText.Length < 1000) + { + if (p == null) + p = new Paragraph(); + + p.Text = currentText.ToString().Trim().Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine); + p.Text = p.Text.Trim('\n', '\r').Trim(); + p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + 3000; + subtitle.Paragraphs.Add(p); + } + subtitle.Renumber(); + } + + private TimeCode DecodeTimeCode(string[] parts) + { + var tc = new TimeCode(0, 0, 0, 0); + try + { + int hour = int.Parse(parts[0]); + int minutes = int.Parse(parts[1]); + int seconds = int.Parse(parts[2]); + int millisecond = int.Parse(parts[3]); + + int milliseconds = (int)(millisecond * 100.0); + if (milliseconds > 999) + milliseconds = 999; + + tc = new TimeCode(hour, minutes, seconds, milliseconds); + } + catch (Exception exception) + { + System.Diagnostics.Debug.WriteLine(exception.Message); + _errorCount++; + } + return tc; + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/F4Xml.cs b/libse/SubtitleFormats/F4Xml.cs new file mode 100644 index 000000000..3b56fa699 --- /dev/null +++ b/libse/SubtitleFormats/F4Xml.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class F4Xml : F4Text + { + public override string Extension + { + get { return ".xml"; } + } + + public override string Name + { + get { return "F4 Xml"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + if (fileName != null && !fileName.EndsWith(Extension, StringComparison.OrdinalIgnoreCase)) + return false; + + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var xml = new XmlDocument(); + var template = @" + + + + +".Replace("'", "\""); + xml.LoadXml(template); + xml.DocumentElement.SelectSingleNode("content").Attributes["content"].Value = ToF4Text(subtitle); + + return ToUtf8XmlString(xml); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + var sb = new StringBuilder(); + foreach (string line in lines) + sb.AppendLine(line); + + string xml = sb.ToString(); + if (!xml.Contains(" lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > 0; + } + + public override string ToText(Subtitle subtitle, string title) + { + string xmlStructure = + "" + Environment.NewLine + + ""; + + var xml = new XmlDocument(); + xml.LoadXml(xmlStructure); + + foreach (Paragraph p in subtitle.Paragraphs) + { + XmlNode paragraph = xml.CreateElement("CuePoint"); + + XmlNode startTime = xml.CreateElement("Time"); + startTime.InnerText = p.StartTime.TotalMilliseconds.ToString(); + paragraph.AppendChild(startTime); + + XmlNode paragraphType = xml.CreateElement("Type"); + paragraphType.InnerText = "event"; + paragraph.AppendChild(paragraphType); + + XmlNode name = xml.CreateElement("Name"); + name.InnerText = p.Text; + paragraph.AppendChild(name); + + XmlNode parameters = xml.CreateElement("Parameters"); + + XmlNode parameter = xml.CreateElement("Parameter"); + name = xml.CreateElement("Name"); + name.InnerText = "source"; + XmlNode value = xml.CreateElement("Value"); + value.InnerText = "transcription"; + parameter.AppendChild(name); + parameter.AppendChild(value); + parameters.AppendChild(parameter); + + parameter = xml.CreateElement("Parameter"); + name = xml.CreateElement("Name"); + name.InnerText = "duration"; + value = xml.CreateElement("Value"); + value.InnerText = p.Duration.TotalMilliseconds.ToString(); + parameter.AppendChild(name); + parameter.AppendChild(value); + parameters.AppendChild(parameter); + + parameter = xml.CreateElement("Parameter"); + name = xml.CreateElement("Name"); + name.InnerText = "confidence"; + value = xml.CreateElement("Value"); + value.InnerText = "50"; + parameter.AppendChild(name); + parameter.AppendChild(value); + parameters.AppendChild(parameter); + + paragraph.AppendChild(parameters); + + xml.DocumentElement.AppendChild(paragraph); + } + + return ToUtf8XmlString(xml); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + + string allText = sb.ToString(); + if (!allText.Contains(" next.StartTime.TotalMilliseconds) + p.EndTime.TotalMilliseconds = next.StartTime.TotalMilliseconds - Configuration.Settings.General.MinimumMillisecondsBetweenLines; + } + } + + subtitle.Renumber(); + } + + } +} diff --git a/libse/SubtitleFormats/FabSubtitler.cs b/libse/SubtitleFormats/FabSubtitler.cs new file mode 100644 index 000000000..d0175932d --- /dev/null +++ b/libse/SubtitleFormats/FabSubtitler.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class FabSubtitler : SubtitleFormat + { + private static readonly Regex regexTimeCodes = new Regex(@"^\d\d:\d\d:\d\d:\d\d \d\d:\d\d:\d\d:\d\d$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "FAB Subtitler"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + int index = 0; + const string writeFormat = "{0} {1}{2}{3}{2}"; + foreach (Paragraph p in subtitle.Paragraphs) + { + //00:50:34:22 00:50:39:13 + //Ich muss dafür sorgen, + //dass die Epsteins weiterleben + sb.AppendLine(string.Format(writeFormat, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), Environment.NewLine, HtmlUtil.RemoveHtmlTags(p.Text))); + index++; + } + return sb.ToString(); + } + + private static string EncodeTimeCode(TimeCode time) + { + //00:50:39:13 (last is frame) + return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", time.Hours, time.Minutes, time.Seconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + //00:03:15:22 00:03:23:10 This is line one. + //This is line two. + Paragraph p = null; + subtitle.Paragraphs.Clear(); + _errorCount = 0; + foreach (string line in lines) + { + if (regexTimeCodes.IsMatch(line)) + { + string temp = line.Substring(0, regexTimeCodes.Match(line).Length); + string start = temp.Substring(0, 11); + string end = temp.Substring(13, 11); + + string[] startParts = start.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + string[] endParts = end.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + if (startParts.Length == 4 && endParts.Length == 4) + { + p = new Paragraph(DecodeTimeCode(startParts), DecodeTimeCode(endParts), string.Empty); + subtitle.Paragraphs.Add(p); + } + } + else if (string.IsNullOrWhiteSpace(line)) + { + // skip these lines + } + else if (p != null) + { + if (string.IsNullOrEmpty(p.Text)) + p.Text = line; + else + p.Text = p.Text + Environment.NewLine + line; + } + } + + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + //00:00:07:12 + var hour = int.Parse(parts[0]); + var minutes = int.Parse(parts[1]); + var seconds = int.Parse(parts[2]); + var frames = int.Parse(parts[3]); + return new TimeCode(hour, minutes, seconds, FramesToMillisecondsMax999(frames)); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/FilmEditXml.cs b/libse/SubtitleFormats/FilmEditXml.cs new file mode 100644 index 000000000..8ddb161a7 --- /dev/null +++ b/libse/SubtitleFormats/FilmEditXml.cs @@ -0,0 +1,177 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class FilmEditXml : SubtitleFormat + { + public override string Extension + { + get { return ".xml"; } + } + + public override string Name + { + get { return "Film Edit xml"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + string xmlAsString = sb.ToString().Trim(); + if (xmlAsString.Contains("") && xmlAsString.Contains("")) + { + var xml = new XmlDocument(); + xml.XmlResolver = null; + try + { + xml.LoadXml(xmlAsString); + var paragraphs = xml.DocumentElement.SelectNodes("subtitle"); + return paragraphs != null && paragraphs.Count > 0 && xml.DocumentElement.Name == "filmeditxml"; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + } + } + return false; + } + + public override string ToText(Subtitle subtitle, string title) + { + string xmlStructure = + "" + Environment.NewLine + + "" + Environment.NewLine + + "Arial" + Environment.NewLine + + "22" + Environment.NewLine + + "720" + Environment.NewLine + + "576" + Environment.NewLine + + "586" + Environment.NewLine + + "330" + Environment.NewLine + + "1420" + Environment.NewLine + + "25" + Environment.NewLine + + "False" + Environment.NewLine + + ""; + + var xml = new XmlDocument(); + xml.LoadXml(xmlStructure); + XmlNode div = xml.DocumentElement; + int no = 0; + foreach (Paragraph p in subtitle.Paragraphs) + { + XmlNode paragraph = xml.CreateElement("subtitle"); + string text = HtmlUtil.RemoveHtmlTags(p.Text); + + XmlNode num = xml.CreateElement("num"); + num.InnerText = no.ToString(); + paragraph.AppendChild(num); + + XmlNode dur = xml.CreateElement("dur"); + num.InnerText = EncodeDuration(p.Duration); + paragraph.AppendChild(num); + + XmlNode textNode = xml.CreateElement("text"); + textNode.InnerText = p.Text.Replace(Environment.NewLine, "\\N"); + paragraph.AppendChild(textNode); + + XmlNode timeIn = xml.CreateElement("in"); + timeIn.InnerText = EncodeTimeCode(p.StartTime); + paragraph.AppendChild(timeIn); + + XmlNode timeOut = xml.CreateElement("out"); + timeOut.InnerText = EncodeTimeCode(p.EndTime); + paragraph.AppendChild(timeOut); + + XmlNode align = xml.CreateElement("align"); + align.InnerText = "C"; + paragraph.AppendChild(align); + + XmlNode posx = xml.CreateElement("posx"); + posx.InnerText = "0"; + paragraph.AppendChild(posx); + + XmlNode post = xml.CreateElement("posy"); + post.InnerText = "308"; + paragraph.AppendChild(post); + + XmlNode memo = xml.CreateElement("memo"); + paragraph.AppendChild(memo); + + div.AppendChild(paragraph); + no++; + } + + return ToUtf8XmlString(xml); + } + + private static string EncodeDuration(TimeCode timeCode) + { + return string.Format("{0:00}:{1:00}", timeCode.Seconds, MillisecondsToFramesMaxFrameRate(timeCode.Milliseconds)); + } + + private static string EncodeTimeCode(TimeCode timeCode) + { + return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", timeCode.Hours, timeCode.Minutes, timeCode.Seconds, MillisecondsToFramesMaxFrameRate(timeCode.Milliseconds)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + var xml = new XmlDocument(); + xml.XmlResolver = null; + xml.LoadXml(sb.ToString().Trim()); + string lastKey = string.Empty; + foreach (XmlNode node in xml.DocumentElement.SelectNodes("subtitle")) + { + try + { + var p = new Paragraph(); + foreach (XmlNode innerNode in node.ChildNodes) + { + switch (innerNode.Name) + { + case "text": + p.Text = innerNode.InnerText.Replace("\\N", Environment.NewLine); + break; + case "in": + p.StartTime = DecodeTime(innerNode.InnerText); + break; + case "out": + p.EndTime = DecodeTime(innerNode.InnerText); + break; + } + } + if (p.StartTime.TotalSeconds >= 0 && p.EndTime.TotalMilliseconds > 0 && !string.IsNullOrEmpty(p.Text)) + subtitle.Paragraphs.Add(p); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + _errorCount++; + } + } + subtitle.Renumber(); + } + + private static TimeCode DecodeTime(string s) + { + var arr = s.Split(':'); + if (arr.Length == 4) + { + return new TimeCode(int.Parse(arr[0]), int.Parse(arr[1]), int.Parse(arr[2]), FramesToMillisecondsMax999(int.Parse(arr[3]))); + } + return new TimeCode(0, 0, 0, 0); + } + + } +} diff --git a/libse/SubtitleFormats/FinalCutProImage.cs b/libse/SubtitleFormats/FinalCutProImage.cs new file mode 100644 index 000000000..ad5f5e2e9 --- /dev/null +++ b/libse/SubtitleFormats/FinalCutProImage.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class FinalCutProImage : SubtitleFormat + { + public double FrameRate { get; set; } + + public override string Extension + { + get { return ".xml"; } + } + + public override string Name + { + get { return "Final Cut Pro Image"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > 0; + } + + public override string ToText(Subtitle subtitle, string title) + { + throw new NotImplementedException(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + FrameRate = Configuration.Settings.General.CurrentFrameRate; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + var xml = new XmlDocument(); + xml.XmlResolver = null; + try + { + xml.LoadXml(sb.ToString().Trim()); + + foreach (XmlNode node in xml.DocumentElement.SelectNodes("sequence/media/video/track/clipitem")) + { + try + { + XmlNode fileNode = node.SelectSingleNode("file"); + if (fileNode != null) + { + XmlNode fileNameNode = fileNode.SelectSingleNode("name"); + XmlNode filePathNode = fileNode.SelectSingleNode("pathurl"); + if (fileNameNode != null) + { + var p = new Paragraph(); + p.Text = fileNameNode.InnerText; + XmlNode inNode = node.SelectSingleNode("in"); + XmlNode startNode = node.SelectSingleNode("start"); + if (inNode != null) + { + p.StartTime.TotalMilliseconds = FramesToMilliseconds(Convert.ToInt32(inNode.InnerText)); + } + else if (startNode != null) + { + p.StartTime.TotalMilliseconds = FramesToMilliseconds(Convert.ToInt32(startNode.InnerText)); + } + XmlNode outNode = node.SelectSingleNode("out"); + XmlNode endNode = node.SelectSingleNode("end"); + if (outNode != null) + { + p.EndTime.TotalMilliseconds = FramesToMilliseconds(Convert.ToInt32(outNode.InnerText)); + } + else if (endNode != null) + { + p.EndTime.TotalMilliseconds = FramesToMilliseconds(Convert.ToInt32(endNode.InnerText)); + } + subtitle.Paragraphs.Add(p); + } + } + } + catch + { + _errorCount++; + } + } + subtitle.Renumber(); + } + catch + { + _errorCount = 1; + return; + } + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/FinalCutProTest2Xml.cs b/libse/SubtitleFormats/FinalCutProTest2Xml.cs new file mode 100644 index 000000000..46ddc7d19 --- /dev/null +++ b/libse/SubtitleFormats/FinalCutProTest2Xml.cs @@ -0,0 +1,296 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + // - Mom, when you were my age what did you want to do? + public class FinalCutProTest2Xml : SubtitleFormat + { + public override string Extension + { + get { return ".xml"; } + } + + public override string Name + { + get { return "Final Cut Pro Test2 Xml"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > 0; + } + + public static string GetFrameRateAsString() + { + if (Configuration.Settings.General.CurrentFrameRate < 24) + return "24"; // ntsc 23.976 + if (Configuration.Settings.General.CurrentFrameRate < 25) + return "24"; + if (Configuration.Settings.General.CurrentFrameRate < 29) + return "25"; + if (Configuration.Settings.General.CurrentFrameRate < 29) + return "25"; + if (Configuration.Settings.General.CurrentFrameRate < 30) + return "30"; // ntsc 29.97 + if (Configuration.Settings.General.CurrentFrameRate < 40) + return "30"; + if (Configuration.Settings.General.CurrentFrameRate < 40) + return "30"; + if (Configuration.Settings.General.CurrentFrameRate < 60) + return "60"; // ntsc 59.94 + return "60"; + } + + public static string GetNtsc() + { + if (Configuration.Settings.General.CurrentFrameRate < 24) + return "TRUE"; // ntsc 23.976 + if (Configuration.Settings.General.CurrentFrameRate < 25) + return "FALSE"; + return "TRUE"; + //if (Configuration.Settings.General.CurrentFrameRate < 29) + // return "FALSE"; + //if (Configuration.Settings.General.CurrentFrameRate < 29) + // return "FALSE"; + //if (Configuration.Settings.General.CurrentFrameRate < 30) + // return "TRUE"; // ntsc 29.97 + //if (Configuration.Settings.General.CurrentFrameRate < 40) + // return "TRUE"; + //if (Configuration.Settings.General.CurrentFrameRate < 40) + // return "TRUE"; + //if (Configuration.Settings.General.CurrentFrameRate < 60) + // return "TRUE"; // ntsc 59.94 + //return "FALSE"; + } + + public override string ToText(Subtitle subtitle, string title) + { + int duration = 0; + if (subtitle.Paragraphs.Count > 0) + duration = (int)Math.Round(subtitle.Paragraphs[subtitle.Paragraphs.Count - 1].EndTime.TotalSeconds * Configuration.Settings.General.CurrentFrameRate); + string seString = "Subtitle Edit at " + DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToShortTimeString(); + string xmlStructure = + "" + Environment.NewLine + + "" + Environment.NewLine + + " " + Environment.NewLine + + " EC466A7D-8B45-4682-9978-D15D630C882Eadd" + seString + "" + duration + ">" + GetNtsc() + @"" + GetFrameRateAsString() + @"" + GetNtsc() + @"" + GetFrameRateAsString() + @"01:00:00:0090000sourceNDF-1-1"; + + const string xmlTrackStructure = "Text3000FALSE251375148615041615FALSEblackFALSETextTextTextgeneratorvideostrTextA finales de los años sesenta, una joven pareja, Guy y Rosemary, fontnameFontLucida GrandefontsizeSize01000[FONTSIZE]fontstyleStyle14Plain1Bold2Italic3Bold/Italic41fontalignAlignment13Left1Center2Right32fontcolorFont Color255255255255originOrigin00fonttrackTracking-2002001leadingLeading-1001000aspectAspect0.151autokernAuto KerningTRUEsubpixelUse SubpixelTRUEBasic MotionbasicmotionmotionvideoscaleScale01000100rotationRotation-864086400centerCenter0.004709580.396648centerOffsetAnchor Point00video3506ED18-CB4D-41B8-A760-4D42356E4F321E6E96FD-94F6-4975-BDFE-7B360E909111"; + + if (string.IsNullOrEmpty(title)) + title = "Subtitle Edit subtitle"; + + var xml = new XmlDocument(); + xml.LoadXml(xmlStructure); + xml.DocumentElement.SelectSingleNode("sequence").Attributes["id"].Value = title; + xml.DocumentElement.SelectSingleNode("sequence/name").InnerText = title; + xml.DocumentElement.SelectSingleNode("sequence/uuid").InnerText = Guid.NewGuid().ToString().ToUpper(); + if (!string.IsNullOrEmpty(subtitle.Header)) + { + var header = new XmlDocument(); + try + { + header.LoadXml(subtitle.Header); + var node = header.DocumentElement.SelectSingleNode("sequence/uuid"); + if (node != null) + xml.DocumentElement.SelectSingleNode("sequence/uuid").InnerText = node.InnerText; + } + catch + { + } + } + + XmlNode trackNode = xml.DocumentElement.SelectSingleNode("sequence/media/video/track[2]"); + + const string newLine = "_____@___"; + int number = 1; + foreach (Paragraph p in subtitle.Paragraphs) + { + XmlNode generatorItem = xml.CreateElement("generatoritem"); + string fontStyle = "1"; //1==plain + var s = HtmlUtil.RemoveOpenCloseTags(p.Text, HtmlUtil.TagFont).Trim(); + if ((s.StartsWith("") && s.EndsWith("")) || (s.StartsWith("") && s.EndsWith(""))) + fontStyle = "4"; //4==bold/italic + else if (s.StartsWith("") && s.EndsWith("")) + fontStyle = "3"; //3==italic + generatorItem.InnerXml = xmlTrackStructure.Replace("[NUMBER]", number.ToString()).Replace("[FONTSTYLE]", fontStyle).Replace("[FONTSIZE]", Configuration.Settings.SubtitleSettings.FcpFontSize.ToString(CultureInfo.InvariantCulture)); + + double frameRate = Configuration.Settings.General.CurrentFrameRate; + XmlNode start = generatorItem.SelectSingleNode("generatoritem/start"); + start.InnerText = ((int)Math.Round(p.StartTime.TotalSeconds * frameRate)).ToString(); + + XmlNode end = generatorItem.SelectSingleNode("generatoritem/end"); + end.InnerText = ((int)Math.Round(p.EndTime.TotalSeconds * frameRate)).ToString(); + + XmlNode text = generatorItem.SelectSingleNode("generatoritem/effect/parameter[parameterid='str']/value"); + text.InnerText = HtmlUtil.RemoveHtmlTags(p.Text); + text.InnerXml = text.InnerXml.Replace(Environment.NewLine, newLine); + + trackNode.AppendChild(generatorItem.SelectSingleNode("generatoritem")); + number++; + } + + string xmlAsText = ToUtf8XmlString(xml); + xmlAsText = xmlAsText.Replace("xmeml[]", "xmeml"); + xmlAsText = xmlAsText.Replace(newLine, " "); + return xmlAsText; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + var frameRate = Configuration.Settings.General.CurrentFrameRate; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + var xml = new XmlDocument(); + xml.XmlResolver = null; + try + { + xml.LoadXml(sb.ToString().Trim()); + + var header = new XmlDocument(); + header.XmlResolver = null; + header.LoadXml(sb.ToString()); + if (header.SelectSingleNode("sequence/media/video/track") != null) + header.RemoveChild(header.SelectSingleNode("sequence/media/video/track")); + subtitle.Header = header.OuterXml; + + if (xml.DocumentElement.SelectSingleNode("sequence/rate") != null && xml.DocumentElement.SelectSingleNode("sequence/rate/timebase") != null) + { + try + { + frameRate = double.Parse(xml.DocumentElement.SelectSingleNode("sequence/rate/timebase").InnerText); + } + catch + { + frameRate = Configuration.Settings.General.CurrentFrameRate; + } + } + + foreach (XmlNode node in xml.SelectNodes("xmeml/sequence/media/video/track")) + { + try + { + foreach (XmlNode generatorItemNode in node.SelectNodes("generatoritem")) + { + XmlNode rate = generatorItemNode.SelectSingleNode("rate"); + if (rate != null) + { + XmlNode timebase = rate.SelectSingleNode("timebase"); + if (timebase != null) + frameRate = double.Parse(timebase.InnerText); + } + + double startFrame = 0; + double endFrame = 0; + XmlNode startNode = generatorItemNode.SelectSingleNode("start"); + if (startNode != null) + startFrame = double.Parse(startNode.InnerText); + + XmlNode endNode = generatorItemNode.SelectSingleNode("end"); + if (endNode != null) + endFrame = double.Parse(endNode.InnerText); + + string text = string.Empty; + foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='str']")) + { + XmlNode valueNode = parameterNode.SelectSingleNode("value"); + if (valueNode != null) + text += valueNode.InnerText; + } + + bool italic = false; + bool bold = false; + foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='style']")) + { + XmlNode valueNode = parameterNode.SelectSingleNode("value"); + var valueEntries = parameterNode.SelectNodes("valuelist/valueentry"); + if (valueNode != null) + { + int no; + if (int.TryParse(valueNode.InnerText, out no)) + { + no--; + if (no < valueEntries.Count) + { + var styleNameNode = valueEntries[no].SelectSingleNode("name"); + if (styleNameNode != null) + { + string styleName = styleNameNode.InnerText.ToLower().Trim(); + italic = styleName == "italic" || styleName == "bold/italic"; + bold = styleName == "bold" || styleName == "bold/italic"; + } + } + } + } + } + if (!bold && !italic) + { + foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='fontstyle']")) + { + XmlNode valueNode = parameterNode.SelectSingleNode("value"); + var valueEntries = parameterNode.SelectNodes("valuelist/valueentry"); + if (valueNode != null) + { + int no; + if (int.TryParse(valueNode.InnerText, out no)) + { + no--; + if (no < valueEntries.Count) + { + var styleNameNode = valueEntries[no].SelectSingleNode("name"); + if (styleNameNode != null) + { + string styleName = styleNameNode.InnerText.ToLower().Trim(); + italic = styleName == "italic" || styleName == "bold/italic"; + bold = styleName == "bold" || styleName == "bold/italic"; + } + } + } + } + } + } + + if (text.Length > 0) + { + if (!text.Contains(Environment.NewLine)) + text = text.Replace("\r", Environment.NewLine); + if (bold) + text = "" + text + ""; + if (italic) + text = "" + text + ""; + subtitle.Paragraphs.Add(new Paragraph(text, Convert.ToDouble((startFrame / frameRate) * 1000), Convert.ToDouble((endFrame / frameRate) * 1000))); + } + } + } + catch + { + _errorCount++; + } + } + subtitle.Renumber(); + } + catch + { + _errorCount = 1; + return; + } + Configuration.Settings.General.CurrentFrameRate = frameRate; + } + + } +} diff --git a/libse/SubtitleFormats/FinalCutProTextXml.cs b/libse/SubtitleFormats/FinalCutProTextXml.cs new file mode 100644 index 000000000..52157143e --- /dev/null +++ b/libse/SubtitleFormats/FinalCutProTextXml.cs @@ -0,0 +1,366 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + // - Mom, when you were my age what did you want to do? + public class FinalCutProTestXml : SubtitleFormat + { + public override string Extension + { + get { return ".xml"; } + } + + public override string Name + { + get { return "Final Cut Pro Test Xml"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > 0; + } + + public static string GetFrameRateAsString() + { + if (Configuration.Settings.General.CurrentFrameRate < 24) + return "24"; // ntsc 23.976 + if (Configuration.Settings.General.CurrentFrameRate < 25) + return "24"; + if (Configuration.Settings.General.CurrentFrameRate < 29) + return "25"; + if (Configuration.Settings.General.CurrentFrameRate < 29) + return "25"; + if (Configuration.Settings.General.CurrentFrameRate < 30) + return "30"; // ntsc 29.97 + if (Configuration.Settings.General.CurrentFrameRate < 40) + return "30"; + if (Configuration.Settings.General.CurrentFrameRate < 40) + return "30"; + if (Configuration.Settings.General.CurrentFrameRate < 60) + return "60"; // ntsc 59.94 + return "60"; + } + + public static string GetNtsc() + { + if (Configuration.Settings.General.CurrentFrameRate < 24) + return "TRUE"; // ntsc 23.976 + if (Configuration.Settings.General.CurrentFrameRate < 25) + return "FALSE"; + return "TRUE"; + //if (Configuration.Settings.General.CurrentFrameRate < 29) + // return "FALSE"; + //if (Configuration.Settings.General.CurrentFrameRate < 29) + // return "FALSE"; + //if (Configuration.Settings.General.CurrentFrameRate < 30) + // return "TRUE"; // ntsc 29.97 + //if (Configuration.Settings.General.CurrentFrameRate < 40) + // return "TRUE"; + //if (Configuration.Settings.General.CurrentFrameRate < 40) + // return "TRUE"; + //if (Configuration.Settings.General.CurrentFrameRate < 60) + // return "TRUE"; // ntsc 59.94 + //return "FALSE"; + } + + public override string ToText(Subtitle subtitle, string title) + { + int duration = 0; + if (subtitle.Paragraphs.Count > 0) + duration = (int)Math.Round(subtitle.Paragraphs[subtitle.Paragraphs.Count - 1].EndTime.TotalSeconds * Configuration.Settings.General.CurrentFrameRate); + + string xmlStructure = + "" + Environment.NewLine + + "" + Environment.NewLine + + "" + Environment.NewLine + + @" 5B3B0C07-9A9D-42AA-872C-C953923F97D8 + add + X + " + duration + @" + + " + GetNtsc() + @" + " + GetFrameRateAsString() + @" + + + + " + GetNtsc() + @" + " + GetFrameRateAsString() + @" + + 00:00:00:00 + 0 + source + NDF + + 0 + " + duration + @" + + + + +"; + + if (string.IsNullOrEmpty(title)) + title = "Subtitle Edit subtitle"; + + string xmlTrackStructure = "Text0" + GetNtsc() + @"" + GetFrameRateAsString() + @"046TextTextTextgeneratorvideostrText[TEXT]fontnameFontArialfontsizeSize0100032fontstyleStyle14Plain1Bold2Italic3Bold/Italic1[FONTSTYLE]fontalignAlignment13Left1Center2Right32fontcolorFont Color1255255255originOrigin00.233854"; + var xml = new XmlDocument(); + xml.LoadXml(xmlStructure); + xml.DocumentElement.SelectSingleNode("sequence").Attributes["id"].Value = title; + xml.DocumentElement.SelectSingleNode("sequence/name").InnerText = title; + + xml.DocumentElement.SelectSingleNode("sequence/uuid").InnerText = Guid.NewGuid().ToString().ToUpper(); + if (!string.IsNullOrEmpty(subtitle.Header)) + { + var header = new XmlDocument(); + try + { + header.LoadXml(subtitle.Header); + var node = header.DocumentElement.SelectSingleNode("sequence/uuid"); + if (node != null) + xml.DocumentElement.SelectSingleNode("sequence/uuid").InnerText = node.InnerText; + } + catch + { + } + } + + XmlNode trackNode = xml.DocumentElement.SelectSingleNode("sequence/media/video/track"); + + const string newLine = "_____@___"; + int number = 1; + foreach (Paragraph p in subtitle.Paragraphs) + { + XmlNode generatorItem = xml.CreateElement("generatoritem"); + string fontStyle = "1"; //1==plain + var s = HtmlUtil.RemoveOpenCloseTags(p.Text, HtmlUtil.TagFont).Trim(); + if ((s.StartsWith("") && s.EndsWith("")) || (s.StartsWith("") && s.EndsWith(""))) + fontStyle = "4"; //4==bold/italic + else if (s.StartsWith("") && s.EndsWith("")) + fontStyle = "3"; //3==italic + generatorItem.InnerXml = xmlTrackStructure.Replace("[NUMBER]", number.ToString()).Replace("[FONTSTYLE]", fontStyle); + + double frameRate = Configuration.Settings.General.CurrentFrameRate; + XmlNode start = generatorItem.SelectSingleNode("generatoritem/start"); + start.InnerText = ((int)Math.Round(p.StartTime.TotalSeconds * frameRate)).ToString(); + + XmlNode end = generatorItem.SelectSingleNode("generatoritem/end"); + end.InnerText = ((int)Math.Round(p.EndTime.TotalSeconds * frameRate)).ToString(); + + XmlNode text = generatorItem.SelectSingleNode("generatoritem/effect/parameter[parameterid='str']/value"); + text.InnerText = HtmlUtil.RemoveHtmlTags(p.Text); + text.InnerXml = text.InnerXml.Replace(Environment.NewLine, newLine); + + trackNode.AppendChild(generatorItem.SelectSingleNode("generatoritem")); + number++; + } + + string xmlAsText = ToUtf8XmlString(xml); + xmlAsText = xmlAsText.Replace("xmeml[]", "xmeml"); + xmlAsText = xmlAsText.Replace(newLine, " "); + return xmlAsText; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + var frameRate = Configuration.Settings.General.CurrentFrameRate; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + var xml = new XmlDocument(); + xml.XmlResolver = null; + try + { + xml.LoadXml(sb.ToString().Trim()); + + var header = new XmlDocument(); + header.XmlResolver = null; + header.LoadXml(sb.ToString()); + if (header.SelectSingleNode("sequence/media/video/track") != null) + header.RemoveChild(header.SelectSingleNode("sequence/media/video/track")); + subtitle.Header = header.OuterXml; + + if (xml.DocumentElement.SelectSingleNode("sequence/rate") != null && xml.DocumentElement.SelectSingleNode("sequence/rate/timebase") != null) + { + try + { + frameRate = double.Parse(xml.DocumentElement.SelectSingleNode("sequence/rate/timebase").InnerText); + } + catch + { + frameRate = Configuration.Settings.General.CurrentFrameRate; + } + } + + foreach (XmlNode node in xml.SelectNodes("xmeml/sequence/media/video/track")) + { + try + { + foreach (XmlNode generatorItemNode in node.SelectNodes("generatoritem")) + { + XmlNode rate = generatorItemNode.SelectSingleNode("rate"); + if (rate != null) + { + XmlNode timebase = rate.SelectSingleNode("timebase"); + if (timebase != null) + frameRate = double.Parse(timebase.InnerText); + } + + double startFrame = 0; + double endFrame = 0; + XmlNode startNode = generatorItemNode.SelectSingleNode("start"); + if (startNode != null) + startFrame = double.Parse(startNode.InnerText); + + XmlNode endNode = generatorItemNode.SelectSingleNode("end"); + if (endNode != null) + endFrame = double.Parse(endNode.InnerText); + + string text = string.Empty; + foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='str']")) + { + XmlNode valueNode = parameterNode.SelectSingleNode("value"); + if (valueNode != null) + text += valueNode.InnerText; + } + + bool italic = false; + bool bold = false; + foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='style']")) + { + XmlNode valueNode = parameterNode.SelectSingleNode("value"); + var valueEntries = parameterNode.SelectNodes("valuelist/valueentry"); + if (valueNode != null) + { + int no; + if (int.TryParse(valueNode.InnerText, out no)) + { + no--; + if (no < valueEntries.Count) + { + var styleNameNode = valueEntries[no].SelectSingleNode("name"); + if (styleNameNode != null) + { + string styleName = styleNameNode.InnerText.ToLower().Trim(); + italic = styleName == "italic" || styleName == "bold/italic"; + bold = styleName == "bold" || styleName == "bold/italic"; + } + } + } + } + } + if (!bold && !italic) + { + foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='fontstyle']")) + { + XmlNode valueNode = parameterNode.SelectSingleNode("value"); + var valueEntries = parameterNode.SelectNodes("valuelist/valueentry"); + if (valueNode != null) + { + int no; + if (int.TryParse(valueNode.InnerText, out no)) + { + no--; + if (no < valueEntries.Count) + { + var styleNameNode = valueEntries[no].SelectSingleNode("name"); + if (styleNameNode != null) + { + string styleName = styleNameNode.InnerText.ToLower().Trim(); + italic = styleName == "italic" || styleName == "bold/italic"; + bold = styleName == "bold" || styleName == "bold/italic"; + } + } + } + } + } + } + + if (text.Length > 0) + { + if (!text.Contains(Environment.NewLine)) + text = text.Replace("\r", Environment.NewLine); + if (bold) + text = "" + text + ""; + if (italic) + text = "" + text + ""; + subtitle.Paragraphs.Add(new Paragraph(text, Convert.ToDouble((startFrame / frameRate) * 1000), Convert.ToDouble((endFrame / frameRate) * 1000))); + } + } + } + catch + { + _errorCount++; + } + } + subtitle.Renumber(); + } + catch + { + _errorCount = 1; + return; + } + Configuration.Settings.General.CurrentFrameRate = frameRate; + } + + } +} diff --git a/libse/SubtitleFormats/FinalCutProXCM.cs b/libse/SubtitleFormats/FinalCutProXCM.cs new file mode 100644 index 000000000..1008b25db --- /dev/null +++ b/libse/SubtitleFormats/FinalCutProXCM.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class FinalCutProXCM : SubtitleFormat + { + public double FrameRate { get; set; } + + public override string Extension + { + get { return ".fcpxml"; } + } + + public override string Name + { + get { return "Final Cut Pro X Chapter Marker"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > 0; + } + + public override string ToText(Subtitle subtitle, string title) + { + if (Configuration.Settings.General.CurrentFrameRate > 26) + FrameRate = 30; + else + FrameRate = 25; + + string xmlStructure = + "" + Environment.NewLine + + "" + Environment.NewLine + + Environment.NewLine + + "" + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + ""; + + var xml = new XmlDocument(); + xml.LoadXml(xmlStructure); + + XmlNode videoNode = xml.DocumentElement.SelectSingleNode("project/sequence/spine/clip"); + + int number = 1; + foreach (Paragraph p in subtitle.Paragraphs) + { + XmlNode chapterMarker = xml.CreateElement("chapter-marker"); + + var attr = xml.CreateAttribute("duration"); + attr.Value = Convert.ToInt64(p.Duration.TotalSeconds * 2400000) + "/2400000s"; + chapterMarker.Attributes.Append(attr); + + attr = xml.CreateAttribute("start"); + attr.Value = Convert.ToInt64(p.StartTime.TotalSeconds * 2400000) + "/2400000s"; + chapterMarker.Attributes.Append(attr); + + attr = xml.CreateAttribute("value"); + attr.Value = p.Text.Replace(Environment.NewLine, Convert.ToChar(8232).ToString()); + chapterMarker.Attributes.Append(attr); + + attr = xml.CreateAttribute("posterOffset"); + attr.Value = "11/24s"; + chapterMarker.Attributes.Append(attr); + + videoNode.AppendChild(chapterMarker); + number++; + } + + string xmlAsText = ToUtf8XmlString(xml); + xmlAsText = xmlAsText.Replace("fcpxml[]", "fcpxml"); + xmlAsText = xmlAsText.Replace("fcpxml []", "fcpxml"); + return xmlAsText; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + FrameRate = Configuration.Settings.General.CurrentFrameRate; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + var xml = new XmlDocument(); + xml.XmlResolver = null; + xml.PreserveWhitespace = true; + try + { + xml.LoadXml(sb.ToString().Trim()); + + foreach (XmlNode node in xml.SelectNodes("fcpxml/project/sequence/spine/clip/chapter-marker")) + { + try + { + var p = new Paragraph(); + p.Text = node.Attributes["value"].InnerText; + p.Text = p.Text.Replace(Convert.ToChar(8232).ToString(), Environment.NewLine); + p.StartTime = DecodeTime(node.Attributes["start"]); + p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + DecodeTime(node.Attributes["duration"]).TotalMilliseconds; + subtitle.Paragraphs.Add(p); + } + catch + { + _errorCount++; + } + } + subtitle.Renumber(); + } + catch + { + _errorCount = 1; + return; + } + } + + private static TimeCode DecodeTime(XmlAttribute duration) + { + // 220220/60000s + if (duration != null) + { + var arr = duration.Value.TrimEnd('s').Split('/'); + if (arr.Length == 2) + { + return TimeCode.FromSeconds(long.Parse(arr[0]) / double.Parse(arr[1])); + } + else if (arr.Length == 1) + { + return TimeCode.FromSeconds(float.Parse(arr[0])); + } + } + return new TimeCode(0, 0, 0, 0); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/FinalCutProXXml.cs b/libse/SubtitleFormats/FinalCutProXXml.cs new file mode 100644 index 000000000..2e0b344c2 --- /dev/null +++ b/libse/SubtitleFormats/FinalCutProXXml.cs @@ -0,0 +1,191 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class FinalCutProXXml : SubtitleFormat + { + public double FrameRate { get; set; } + + public override string Extension + { + get { return ".fcpxml"; } + } + + public override string Name + { + get { return "Final Cut Pro X Xml"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > 0; + } + + public override string ToText(Subtitle subtitle, string title) + { + if (Configuration.Settings.General.CurrentFrameRate > 26) + FrameRate = 30; + else + FrameRate = 25; + + string xmlStructure = + "" + Environment.NewLine + + "" + Environment.NewLine + + Environment.NewLine + + "" + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + // + // + // + // + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + ""; + + string xmlClipStructure = + " " + Environment.NewLine + + " <adjust-transform position=\"0.267518 -32.3158\"/>" + Environment.NewLine + + " <text></text>" + Environment.NewLine + + " "; + + var xml = new XmlDocument(); + xml.LoadXml(xmlStructure); + + XmlNode videoNode = xml.DocumentElement.SelectSingleNode("project/sequence/spine"); + + int number = 1; + foreach (Paragraph p in subtitle.Paragraphs) + { + XmlNode clip = xml.CreateElement("clip"); + clip.InnerXml = xmlClipStructure; + var attr = xml.CreateAttribute("name"); + attr.Value = title; + clip.Attributes.Append(attr); + + attr = xml.CreateAttribute("duration"); + //attr.Value = "9529520/2400000s"; + attr.Value = Convert.ToInt64(p.Duration.TotalSeconds * 2400000) + "/2400000s"; + clip.Attributes.Append(attr); + + attr = xml.CreateAttribute("start"); + //attr.Value = "1201200/2400000s"; + attr.Value = Convert.ToInt64(p.StartTime.TotalSeconds * 2400000) + "/2400000s"; + clip.Attributes.Append(attr); + + attr = xml.CreateAttribute("audioStart"); + attr.Value = "0s"; + clip.Attributes.Append(attr); + + attr = xml.CreateAttribute("audioDuration"); + attr.Value = Convert.ToInt64(p.Duration.TotalSeconds * 2400000) + "/2400000s"; + clip.Attributes.Append(attr); + + attr = xml.CreateAttribute("tcFormat"); + attr.Value = "NDF"; + clip.Attributes.Append(attr); + + XmlNode titleNode = clip.SelectSingleNode("title"); + titleNode.Attributes["offset"].Value = Convert.ToInt64(p.StartTime.TotalSeconds * 60000) + "/60000s"; + titleNode.Attributes["name"].Value = HtmlUtil.RemoveHtmlTags(p.Text); + titleNode.Attributes["duration"].Value = Convert.ToInt64(p.Duration.TotalSeconds * 60000) + "/60000s"; + titleNode.Attributes["start"].Value = Convert.ToInt64(p.StartTime.TotalSeconds * 60000) + "/60000s"; + + XmlNode text = clip.SelectSingleNode("title/text"); + text.InnerText = HtmlUtil.RemoveHtmlTags(p.Text); + + videoNode.AppendChild(clip); + number++; + } + + string xmlAsText = ToUtf8XmlString(xml); + xmlAsText = xmlAsText.Replace("fcpxml[]", "fcpxml"); + xmlAsText = xmlAsText.Replace("fcpxml []", "fcpxml"); + return xmlAsText; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + FrameRate = Configuration.Settings.General.CurrentFrameRate; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + var xml = new XmlDocument(); + xml.XmlResolver = null; + try + { + xml.LoadXml(sb.ToString().Trim()); + + foreach (XmlNode node in xml.SelectNodes("fcpxml/project/sequence/spine/clip")) + { + try + { + foreach (XmlNode title in node.SelectNodes("title")) + { + var role = title.Attributes["role"]; + if (role != null && role.InnerText == "Subtitles") + { + var textNode = title.SelectSingleNode("text"); + if (textNode != null && !string.IsNullOrEmpty(textNode.InnerText)) + { + string text = textNode.InnerText; + Paragraph p = new Paragraph(); + p.Text = text.Trim(); + p.StartTime = DecodeTime(title.Attributes["offset"]); + p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + DecodeTime(title.Attributes["duration"]).TotalMilliseconds; + subtitle.Paragraphs.Add(p); + } + } + } + } + catch + { + _errorCount++; + } + } + subtitle.Renumber(); + } + catch + { + _errorCount = 1; + return; + } + } + + private static TimeCode DecodeTime(XmlAttribute duration) + { + // 220220/60000s + if (duration != null) + { + var arr = duration.Value.TrimEnd('s').Split('/'); + if (arr.Length == 2) + { + return TimeCode.FromSeconds(long.Parse(arr[0]) / double.Parse(arr[1])); + } + else if (arr.Length == 1) + { + return TimeCode.FromSeconds(float.Parse(arr[0])); + } + } + return new TimeCode(0, 0, 0, 0); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/FinalCutProXml.cs b/libse/SubtitleFormats/FinalCutProXml.cs new file mode 100644 index 000000000..b0fc7c570 --- /dev/null +++ b/libse/SubtitleFormats/FinalCutProXml.cs @@ -0,0 +1,617 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + // - Mom, when you were my age what did you want to do? + public class FinalCutProXml : SubtitleFormat + { + public override string Extension + { + get { return ".xml"; } + } + + public override string Name + { + get { return "Final Cut Pro Xml"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > 0; + } + + public static string GetFrameRateAsString() + { + if (Configuration.Settings.General.CurrentFrameRate < 24) + return "24"; // ntsc 23.976 + if (Configuration.Settings.General.CurrentFrameRate < 25) + return "24"; + if (Configuration.Settings.General.CurrentFrameRate < 29) + return "25"; + if (Configuration.Settings.General.CurrentFrameRate < 29) + return "25"; + if (Configuration.Settings.General.CurrentFrameRate < 30) + return "30"; // ntsc 29.97 + if (Configuration.Settings.General.CurrentFrameRate < 40) + return "30"; + if (Configuration.Settings.General.CurrentFrameRate < 40) + return "30"; + if (Configuration.Settings.General.CurrentFrameRate < 60) + return "60"; // ntsc 59.94 + return "60"; + } + + public static string GetNtsc() + { + if (Configuration.Settings.General.CurrentFrameRate < 24) + return "TRUE"; // ntsc 23.976 + if (Configuration.Settings.General.CurrentFrameRate < 25) + return "FALSE"; + return "TRUE"; + } + + public override string ToText(Subtitle subtitle, string title) + { + int duration = 0; + if (subtitle.Paragraphs.Count > 0) + duration = (int)Math.Round(subtitle.Paragraphs[subtitle.Paragraphs.Count - 1].EndTime.TotalSeconds * Configuration.Settings.General.CurrentFrameRate); + + string xmlStructure = + "" + Environment.NewLine + + "" + Environment.NewLine + + "" + Environment.NewLine + + @" 5B3B0C07-9A9D-42AA-872C-C953923F97D8 + add + X + " + duration + @" + + " + GetNtsc() + @" + " + GetFrameRateAsString() + @" + + + + " + GetNtsc() + @" + " + GetFrameRateAsString() + @" + + 00:00:00:00 + 0 + source + NDF + + 0 + " + duration + @" + + + + +"; + + string xmlTrackStructure = + @" + Outline Text + 3000 + + " + GetNtsc() + @" + " + GetFrameRateAsString() + @" + + 1380 + 1474 + 8228 + 8322 + TRUE + FALSE + black + Outline Text1 + + Outline Text + Outline Text + Text + generator + video + + part1 + Text Settings + + + + str + Text + [TEXT] + + + font + Font + [FONTNAME] + + + style + Style + 1 + 4 + + + Plain + 1 + + + Bold + 2 + + + Italic + 3 + + + Bold/Italic + 4 + + + [FONTSTYLE] + + + align + Alignment + 1 + 3 + + + Left + 1 + + + Center + 2 + + + Right + 3 + + + 2 + + + size + Size + 0 + 200 + [FONTSIZE] + + + track + Tracking + 0 + 100 + 1 + + + lead + Leading + -100 + 100 + 0 + + + aspect + Aspect + 0 + 4 + 1 + + + linewidth + Line Width + 0 + 200 + 20 + + + linesoft + Line Softness + 0 + 100 + 5 + + + textopacity + Text Opacity + 0 + 100 + 100 + + + center + Center + + 0.00833333 + 0.390741 + + + + textcolor + Text Color + + 255 + 255 + 255 + 255 + + + + supertext + Text Graphic + + + linecolor + Line Color + + 255 + 0 + 0 + 0 + + + + part2 + Background Settings + + + + xscale + Horizontal Size + 0 + 200 + 0 + + + yscale + Vertical Size + 0 + 200 + 0 + + + xoffset + Horizontal Offset + -100 + 100 + 0 + + + yoffset + Vertical Offset + -100 + 100 + 0 + + + backcolor + Back Color + + 255 + 255 + 255 + 255 + + + + superback + Back Graphic + + + crop + Crop + FALSE + + + autokern + Auto Kerning + TRUE + + + + video + + "; + + if (string.IsNullOrEmpty(title)) + title = "Subtitle Edit subtitle"; + + var xml = new XmlDocument(); + xml.LoadXml(xmlStructure); + xml.DocumentElement.SelectSingleNode("sequence").Attributes["id"].Value = title; + xml.DocumentElement.SelectSingleNode("sequence/name").InnerText = title; + xml.DocumentElement.SelectSingleNode("sequence/uuid").InnerText = Guid.NewGuid().ToString().ToUpper(); + if (!string.IsNullOrEmpty(subtitle.Header)) + { + var header = new XmlDocument(); + try + { + header.LoadXml(subtitle.Header); + var node = header.DocumentElement.SelectSingleNode("sequence/uuid"); + if (node != null) + xml.DocumentElement.SelectSingleNode("sequence/uuid").InnerText = node.InnerText; + } + catch + { + } + } + + XmlNode trackNode = xml.DocumentElement.SelectSingleNode("sequence/media/video/track"); + + const string newLine = "_____@___"; + int number = 1; + foreach (Paragraph p in subtitle.Paragraphs) + { + XmlNode generatorItem = xml.CreateElement("generatoritem"); + string fontStyle = "1"; //1==plain + var s = HtmlUtil.RemoveOpenCloseTags(p.Text, HtmlUtil.TagFont).Trim(); + if ((s.StartsWith("") && s.EndsWith("")) || (s.StartsWith("") && s.EndsWith(""))) + fontStyle = "4"; //4==bold/italic + else if (s.StartsWith("") && s.EndsWith("")) + fontStyle = "3"; //3==italic + generatorItem.InnerXml = xmlTrackStructure.Replace("[NUMBER]", number.ToString()).Replace("[FONTSTYLE]", fontStyle). + Replace("[FONTSIZE]", Configuration.Settings.SubtitleSettings.FcpFontSize.ToString(CultureInfo.InvariantCulture)). + Replace("[FONTNAME]", Configuration.Settings.SubtitleSettings.FcpFontName). + Replace("[NUMBER]", number.ToString(CultureInfo.InvariantCulture)); + + double frameRate = Configuration.Settings.General.CurrentFrameRate; + XmlNode start = generatorItem.SelectSingleNode("generatoritem/start"); + start.InnerText = ((int)Math.Round(p.StartTime.TotalSeconds * frameRate)).ToString(); + + XmlNode end = generatorItem.SelectSingleNode("generatoritem/end"); + end.InnerText = ((int)Math.Round(p.EndTime.TotalSeconds * frameRate)).ToString(); + + XmlNode text = generatorItem.SelectSingleNode("generatoritem/effect/parameter[parameterid='str']/value"); + text.InnerText = HtmlUtil.RemoveHtmlTags(p.Text); + text.InnerXml = text.InnerXml.Replace(Environment.NewLine, newLine); + + trackNode.AppendChild(generatorItem.SelectSingleNode("generatoritem")); + number++; + } + + string xmlAsText = ToUtf8XmlString(xml); + xmlAsText = xmlAsText.Replace("xmeml[]", "xmeml"); + xmlAsText = xmlAsText.Replace(newLine, " "); + return xmlAsText; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + var frameRate = Configuration.Settings.General.CurrentFrameRate; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + var xml = new XmlDocument(); + xml.XmlResolver = null; + try + { + xml.LoadXml(sb.ToString().Trim()); + var header = new XmlDocument(); + header.XmlResolver = null; + header.LoadXml(sb.ToString()); + if (header.SelectSingleNode("sequence/media/video/track") != null) + header.RemoveChild(header.SelectSingleNode("sequence/media/video/track")); + subtitle.Header = header.OuterXml; + + if (xml.DocumentElement.SelectSingleNode("sequence/rate") != null && xml.DocumentElement.SelectSingleNode("sequence/rate/timebase") != null) + { + try + { + frameRate = double.Parse(xml.DocumentElement.SelectSingleNode("sequence/rate/timebase").InnerText); + } + catch + { + frameRate = Configuration.Settings.General.CurrentFrameRate; + } + } + + foreach (XmlNode node in xml.SelectNodes("xmeml/sequence/media/video/track")) + { + try + { + foreach (XmlNode generatorItemNode in node.SelectNodes("generatoritem")) + { + XmlNode rate = generatorItemNode.SelectSingleNode("rate"); + if (rate != null) + { + XmlNode timebase = rate.SelectSingleNode("timebase"); + if (timebase != null) + frameRate = double.Parse(timebase.InnerText); + } + + double startFrame = 0; + double endFrame = 0; + XmlNode startNode = generatorItemNode.SelectSingleNode("start"); + if (startNode != null) + startFrame = double.Parse(startNode.InnerText); + + XmlNode endNode = generatorItemNode.SelectSingleNode("end"); + if (endNode != null) + endFrame = double.Parse(endNode.InnerText); + + string text = string.Empty; + foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='str']")) + { + XmlNode valueNode = parameterNode.SelectSingleNode("value"); + if (valueNode != null) + text += valueNode.InnerText; + } + if (text.Length == 0) + { + foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='str1']")) + { + XmlNode valueNode = parameterNode.SelectSingleNode("value"); + if (valueNode != null) + text += valueNode.InnerText; + } + } + if (text.Length == 0) + { + foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='str2']")) + { + XmlNode valueNode = parameterNode.SelectSingleNode("value"); + if (valueNode != null) + text += valueNode.InnerText; + } + } + if (text.Length == 0) + { + foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='sourcetext']")) + { + XmlNode valueNode = parameterNode.SelectSingleNode("value"); + if (valueNode != null) + text += valueNode.InnerText; + } + } + if (text.Length == 0) + { + foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='text']")) + { + XmlNode valueNode = parameterNode.SelectSingleNode("value"); + if (valueNode != null) + text += valueNode.InnerText; + } + } + + bool italic = false; + bool bold = false; + foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='style']")) + { + XmlNode valueNode = parameterNode.SelectSingleNode("value"); + var valueEntries = parameterNode.SelectNodes("valuelist/valueentry"); + if (valueNode != null) + { + int no; + if (int.TryParse(valueNode.InnerText, out no)) + { + no--; + if (no < valueEntries.Count) + { + var styleNameNode = valueEntries[no].SelectSingleNode("name"); + if (styleNameNode != null) + { + string styleName = styleNameNode.InnerText.ToLower().Trim(); + italic = styleName == "italic" || styleName == "bold/italic"; + bold = styleName == "bold" || styleName == "bold/italic"; + } + } + } + } + } + if (!bold && !italic) + { + foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='fontstyle']")) + { + XmlNode valueNode = parameterNode.SelectSingleNode("value"); + var valueEntries = parameterNode.SelectNodes("valuelist/valueentry"); + if (valueNode != null) + { + int no; + if (int.TryParse(valueNode.InnerText, out no)) + { + no--; + if (no < valueEntries.Count) + { + var styleNameNode = valueEntries[no].SelectSingleNode("name"); + if (styleNameNode != null) + { + string styleName = styleNameNode.InnerText.ToLower().Trim(); + italic = styleName == "italic" || styleName == "bold/italic"; + bold = styleName == "bold" || styleName == "bold/italic"; + } + } + } + } + } + } + + if (text.Length > 0) + { + if (!text.Contains(Environment.NewLine)) + text = text.Replace("\r", Environment.NewLine); + if (bold) + text = "" + text + ""; + if (italic) + text = "" + text + ""; + subtitle.Paragraphs.Add(new Paragraph(text, Convert.ToDouble((startFrame / frameRate) * 1000), Convert.ToDouble((endFrame / frameRate) * 1000))); + } + } + } + catch + { + _errorCount++; + } + } + subtitle.Renumber(); + } + catch + { + _errorCount = 1; + return; + } + Configuration.Settings.General.CurrentFrameRate = frameRate; + } + + } +} diff --git a/libse/SubtitleFormats/FinalCutProXml13.cs b/libse/SubtitleFormats/FinalCutProXml13.cs new file mode 100644 index 000000000..31f6ca4da --- /dev/null +++ b/libse/SubtitleFormats/FinalCutProXml13.cs @@ -0,0 +1,210 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class FinalCutProXml13 : SubtitleFormat + { + public double FrameRate { get; set; } + + public override string Extension + { + get { return ".fcpxml"; } + } + + public override string Name + { + get { return "Final Cut Pro Xml 1.3"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > 0; + } + + public override string ToText(Subtitle subtitle, string title) + { + if (Configuration.Settings.General.CurrentFrameRate > 26) + FrameRate = 30; + else + FrameRate = 25; + + string xmlStructure = + "" + Environment.NewLine + + "" + Environment.NewLine + + Environment.NewLine + + "" + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + ""; + + string xmlClipStructure = + " "; + + var xml = new XmlDocument(); + xml.LoadXml(xmlStructure); + + XmlNode videoNode = xml.DocumentElement.SelectSingleNode("project/sequence/spine"); + + int number = 1; + foreach (Paragraph p in subtitle.Paragraphs) + { + XmlNode clip = xml.CreateElement("clip"); + clip.InnerXml = xmlClipStructure; + var attr = xml.CreateAttribute("name"); + attr.Value = title; + clip.Attributes.Append(attr); + + attr = xml.CreateAttribute("duration"); + //attr.Value = "9529520/2400000s"; + attr.Value = Convert.ToInt64(p.Duration.TotalSeconds * 2400000) + "/2400000s"; + clip.Attributes.Append(attr); + + attr = xml.CreateAttribute("start"); + //attr.Value = "1201200/2400000s"; + attr.Value = Convert.ToInt64(p.StartTime.TotalSeconds * 2400000) + "/2400000s"; + clip.Attributes.Append(attr); + + attr = xml.CreateAttribute("audioStart"); + attr.Value = "0s"; + clip.Attributes.Append(attr); + + attr = xml.CreateAttribute("audioDuration"); + attr.Value = Convert.ToInt64(p.Duration.TotalSeconds * 2400000) + "/2400000s"; + clip.Attributes.Append(attr); + + attr = xml.CreateAttribute("tcFormat"); + attr.Value = "NDF"; + clip.Attributes.Append(attr); + + XmlNode titleNode = clip.SelectSingleNode("video"); + titleNode.Attributes["offset"].Value = Convert.ToInt64(p.StartTime.TotalSeconds * 60000) + "/60000s"; + titleNode.Attributes["name"].Value = HtmlUtil.RemoveHtmlTags(p.Text); + titleNode.Attributes["duration"].Value = Convert.ToInt64(p.Duration.TotalSeconds * 60000) + "/60000s"; + titleNode.Attributes["start"].Value = Convert.ToInt64(p.StartTime.TotalSeconds * 60000) + "/60000s"; + + XmlNode param = clip.SelectSingleNode("video/param"); + param.Attributes["value"].InnerText = HtmlUtil.RemoveHtmlTags(p.Text); + + videoNode.AppendChild(clip); + number++; + } + + string xmlAsText = ToUtf8XmlString(xml); + xmlAsText = xmlAsText.Replace("fcpxml[]", "fcpxml"); + xmlAsText = xmlAsText.Replace("fcpxml []", "fcpxml"); + return xmlAsText; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + FrameRate = Configuration.Settings.General.CurrentFrameRate; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + string x = sb.ToString(); + if (!x.Contains(" 0) + { + var prev = subtitle.Paragraphs[subtitle.Paragraphs.Count - 1]; + if (prev.Text == p.Text && prev.StartTime.TotalMilliseconds == p.StartTime.TotalMilliseconds) + add = false; + } + if (add) + subtitle.Paragraphs.Add(p); + } + catch + { + _errorCount++; + } + } + } + subtitle.Renumber(); + } + catch (Exception exception) + { + System.Diagnostics.Debug.WriteLine(exception.Message); + _errorCount = 1; + return; + } + } + + private static TimeCode DecodeTime(XmlAttribute duration) + { + // 220220/60000s + if (duration != null) + { + var arr = duration.Value.TrimEnd('s').Split('/'); + if (arr.Length == 2) + { + return TimeCode.FromSeconds(long.Parse(arr[0]) / double.Parse(arr[1])); + } + else if (arr.Length == 1) + { + return TimeCode.FromSeconds(float.Parse(arr[0])); + } + } + return new TimeCode(0, 0, 0, 0); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/FinalCutProXml14.cs b/libse/SubtitleFormats/FinalCutProXml14.cs new file mode 100644 index 000000000..875283135 --- /dev/null +++ b/libse/SubtitleFormats/FinalCutProXml14.cs @@ -0,0 +1,197 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class FinalCutProXml14 : SubtitleFormat + { + public double FrameRate { get; set; } + + public override string Extension + { + get { return ".fcpxml"; } + } + + public override string Name + { + get { return "Final Cut Pro Xml 1.4"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > 0; + } + + public override string ToText(Subtitle subtitle, string title) + { + if (Configuration.Settings.General.CurrentFrameRate > 26) + FrameRate = 30; + else + FrameRate = 25; + + string xmlStructure = + "" + Environment.NewLine + + "" + Environment.NewLine + + Environment.NewLine + + "" + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " Apple ProRes 422 Proxy" + Environment.NewLine + + " Linear PCM" + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + // From here down I am unsure how it should be + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + ""; + + string xmlClipStructure = + " "; + + var xml = new XmlDocument(); + xml.LoadXml(xmlStructure); + XmlNode videoNode = xml.DocumentElement.SelectSingleNode("//project/sequence/spine/clip"); + int number = 1; + foreach (Paragraph p in subtitle.Paragraphs) + { + XmlNode video = xml.CreateElement("video"); + video.InnerXml = xmlClipStructure; + + XmlNode generatorNode = video.SelectSingleNode("video"); + generatorNode.Attributes["offset"].Value = Convert.ToInt64(p.StartTime.TotalSeconds * 2400000) + "/2400000s"; + generatorNode.Attributes["duration"].Value = Convert.ToInt64(p.Duration.TotalSeconds * 2400000) + "/2400000s"; + generatorNode.Attributes["start"].Value = Convert.ToInt64(p.StartTime.TotalSeconds * 2400000) + "/2400000s"; + + XmlNode param = video.SelectSingleNode("video/param"); + param.Attributes["value"].InnerText = HtmlUtil.RemoveHtmlTags(p.Text); + + videoNode.AppendChild(generatorNode); + number++; + } + + string xmlAsText = ToUtf8XmlString(xml); + xmlAsText = xmlAsText.Replace("fcpxml[]", "fcpxml"); + xmlAsText = xmlAsText.Replace("fcpxml []", "fcpxml"); + return xmlAsText; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + FrameRate = Configuration.Settings.General.CurrentFrameRate; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + string x = sb.ToString(); + if (!x.Contains("") && !x.Contains("")) + return; + + var xml = new XmlDocument(); + xml.XmlResolver = null; + try + { + xml.LoadXml(x.Trim()); + + foreach (XmlNode node in xml.SelectNodes("//project/sequence/spine/clip/video/param[@name='Text']")) + { + try + { + string text = node.Attributes["value"].InnerText; + Paragraph p = new Paragraph(); + p.Text = text.Trim(); + p.StartTime = DecodeTime(node.ParentNode.Attributes["offset"]); + p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + DecodeTime(node.ParentNode.Attributes["duration"]).TotalMilliseconds; + subtitle.Paragraphs.Add(p); + } + catch + { + _errorCount++; + } + } + + if (subtitle.Paragraphs.Count == 0) + { + foreach (XmlNode node in xml.SelectNodes("//project/sequence/spine/clip/video/title/text")) + { + try + { + string text = node.ParentNode.InnerText; + Paragraph p = new Paragraph(); + p.Text = text.Trim(); + p.StartTime = DecodeTime(node.ParentNode.Attributes["offset"]); + p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + DecodeTime(node.ParentNode.Attributes["duration"]).TotalMilliseconds; + bool add = true; + if (subtitle.Paragraphs.Count > 0) + { + var prev = subtitle.Paragraphs[subtitle.Paragraphs.Count - 1]; + if (prev.Text == p.Text && prev.StartTime.TotalMilliseconds == p.StartTime.TotalMilliseconds) + add = false; + } + if (add) + subtitle.Paragraphs.Add(p); + } + catch + { + _errorCount++; + } + } + } + subtitle.Renumber(); + } + catch + { + _errorCount = 1; + return; + } + } + + private static TimeCode DecodeTime(XmlAttribute duration) + { + // 220220/60000s + if (duration != null) + { + var arr = duration.Value.TrimEnd('s').Split('/'); + if (arr.Length == 2) + { + return TimeCode.FromSeconds(long.Parse(arr[0]) / double.Parse(arr[1])); + } + else if (arr.Length == 1) + { + return TimeCode.FromSeconds(float.Parse(arr[0])); + } + } + return new TimeCode(0, 0, 0, 0); + } + + } +} diff --git a/libse/SubtitleFormats/FinalCutProXml14Text.cs b/libse/SubtitleFormats/FinalCutProXml14Text.cs new file mode 100644 index 000000000..aea2749fc --- /dev/null +++ b/libse/SubtitleFormats/FinalCutProXml14Text.cs @@ -0,0 +1,198 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class FinalCutProXml14Text : SubtitleFormat + { + public double FrameRate { get; set; } + + public override string Extension + { + get { return ".fcpxml"; } + } + + public override string Name + { + get { return "Final Cut Pro Xml 1.4 Text"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > 0; + } + + public override string ToText(Subtitle subtitle, string title) + { + if (Configuration.Settings.General.CurrentFrameRate > 26) + FrameRate = 30; + else + FrameRate = 25; + + string xmlStructure = + "" + Environment.NewLine + + "" + Environment.NewLine + + Environment.NewLine + + "" + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + //" " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + ""; + + string xmlClipStructure = + "" + Environment.NewLine + + " <param name=\"Position\" key=\"9999/999166631/999166633/1/100/101\" value=\"-1.67499 -470.934\"/>" + Environment.NewLine + + " <text>" + Environment.NewLine + + " <text-style ref=\"ts[NUMBER]\">THE NOISEMAKER</text-style>" + Environment.NewLine + + " </text>" + Environment.NewLine + + " <text-style-def id=\"ts[NUMBER]\">" + Environment.NewLine + + " <text-style font=\"Lucida Grande\" fontSize=\"36\" fontFace=\"Regular\" fontColor=\"0.793266 0.793391 0.793221 1\" baseline=\"29\" shadowColor=\"0 0 0 1\" shadowOffset=\"5 315\" alignment=\"center\"/>" + Environment.NewLine + + " </text-style-def>" + Environment.NewLine + + ""; + + var xml = new XmlDocument(); + xml.LoadXml(xmlStructure); + XmlNode videoNode = xml.DocumentElement.SelectSingleNode("//project/sequence/spine/gap"); + int number = 1; + foreach (Paragraph p in subtitle.Paragraphs) + { + XmlNode video = xml.CreateElement("video"); + var trimmedTitle = new StringBuilder(); + foreach (var ch in HtmlUtil.RemoveHtmlTags(p.Text, true)) + { + if ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".Contains(ch.ToString(CultureInfo.InvariantCulture))) + trimmedTitle.Append(ch.ToString(CultureInfo.InvariantCulture)); + } + string temp = xmlClipStructure.Replace("[NUMBER]", number.ToString(CultureInfo.InvariantCulture)).Replace("[TITLEID]", trimmedTitle.ToString()); + video.InnerXml = temp; + + XmlNode generatorNode = video.SelectSingleNode("title"); + if (IsNearleWholeNumber(p.StartTime.TotalSeconds)) + generatorNode.Attributes["offset"].Value = Convert.ToInt64(p.StartTime.TotalSeconds) + "s"; + else + generatorNode.Attributes["offset"].Value = Convert.ToInt64(p.StartTime.TotalSeconds * 2400000) + "/2400000s"; + + if (IsNearleWholeNumber(p.Duration.TotalSeconds)) + generatorNode.Attributes["duration"].Value = Convert.ToInt64(p.Duration.TotalSeconds) + "s"; + else + generatorNode.Attributes["duration"].Value = Convert.ToInt64(p.Duration.TotalSeconds * 2400000) + "/2400000s"; + + if (IsNearleWholeNumber(p.StartTime.TotalSeconds)) + generatorNode.Attributes["start"].Value = Convert.ToInt64(p.StartTime.TotalSeconds) + "s"; + else + generatorNode.Attributes["start"].Value = Convert.ToInt64(p.StartTime.TotalSeconds * 2400000) + "/2400000s"; + + XmlNode param = video.SelectSingleNode("title/text/text-style"); + param.InnerText = HtmlUtil.RemoveHtmlTags(p.Text); + + videoNode.AppendChild(generatorNode); + number++; + } + + string xmlAsText = ToUtf8XmlString(xml); + xmlAsText = xmlAsText.Replace("fcpxml[]", "fcpxml"); + xmlAsText = xmlAsText.Replace("fcpxml []", "fcpxml"); + return xmlAsText; + } + + private static bool IsNearleWholeNumber(double number) + { + double rest = number - Convert.ToInt64(number); + return rest < 0.001; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + FrameRate = Configuration.Settings.General.CurrentFrameRate; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + string x = sb.ToString(); + if (!x.Contains("") && !x.Contains("")) + return; + + var xml = new XmlDocument(); + try + { + xml.LoadXml(x.Trim()); + + if (subtitle.Paragraphs.Count == 0) + { + foreach (XmlNode node in xml.SelectNodes("//project/sequence/spine/gap/title/text")) + { + try + { + string text = node.ParentNode.InnerText; + var p = new Paragraph(); + p.Text = text.Trim(); + p.StartTime = DecodeTime(node.ParentNode.Attributes["offset"]); + p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + DecodeTime(node.ParentNode.Attributes["duration"]).TotalMilliseconds; + bool add = true; + if (subtitle.Paragraphs.Count > 0) + { + var prev = subtitle.Paragraphs[subtitle.Paragraphs.Count - 1]; + if (prev.Text == p.Text && prev.StartTime.TotalMilliseconds == p.StartTime.TotalMilliseconds) + add = false; + } + if (add) + subtitle.Paragraphs.Add(p); + } + catch + { + _errorCount++; + } + } + } + subtitle.Renumber(); + } + catch + { + _errorCount = 1; + } + } + + private static TimeCode DecodeTime(XmlAttribute duration) + { + // 220220/60000s + if (duration != null) + { + var arr = duration.Value.TrimEnd('s').Split('/'); + if (arr.Length == 2) + { + return TimeCode.FromSeconds(long.Parse(arr[0]) / double.Parse(arr[1])); + } + if (arr.Length == 1) + { + return TimeCode.FromSeconds(float.Parse(arr[0])); + } + } + return new TimeCode(0, 0, 0, 0); + } + + } +} diff --git a/libse/SubtitleFormats/FinalCutProXmlGap.cs b/libse/SubtitleFormats/FinalCutProXmlGap.cs new file mode 100644 index 000000000..c0ab24e17 --- /dev/null +++ b/libse/SubtitleFormats/FinalCutProXmlGap.cs @@ -0,0 +1,163 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class FinalCutProXmlGap : SubtitleFormat + { + public double FrameRate { get; set; } + + public override string Extension + { + get { return ".fcpxml"; } + } + + public override string Name + { + get { return "Final Cut Xml Gap"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > 0; + } + + public override string ToText(Subtitle subtitle, string title) + { + if (Configuration.Settings.General.CurrentFrameRate > 26) + FrameRate = 30; + else + FrameRate = 25; + + string xmlStructure = + "" + Environment.NewLine + + "" + Environment.NewLine + + Environment.NewLine + + "" + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + // + // + // + // + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + ""; + + string xmlClipStructure = + " " + Environment.NewLine + + " <adjust-transform position=\"0.267518 -32.3158\"/>" + Environment.NewLine + + " <text></text>" + Environment.NewLine + + " "; + + var xml = new XmlDocument(); + xml.LoadXml(xmlStructure); + + XmlNode videoNode = xml.DocumentElement.SelectSingleNode("project/sequence/spine/gap"); + + int number = 1; + foreach (Paragraph p in subtitle.Paragraphs) + { + XmlNode titleNode = xml.CreateElement("holder"); + titleNode.InnerXml = xmlClipStructure; + titleNode = titleNode.SelectSingleNode("title"); + titleNode.Attributes["offset"].Value = Convert.ToInt64(p.StartTime.TotalSeconds * 60000) + "/60000s"; + titleNode.Attributes["name"].Value = HtmlUtil.RemoveHtmlTags(p.Text); + titleNode.Attributes["duration"].Value = Convert.ToInt64(p.Duration.TotalSeconds * 60000) + "/60000s"; + titleNode.Attributes["start"].Value = Convert.ToInt64(p.StartTime.TotalSeconds * 60000) + "/60000s"; + titleNode.SelectSingleNode("text").InnerText = HtmlUtil.RemoveHtmlTags(p.Text); + videoNode.AppendChild(titleNode); + number++; + } + + string xmlAsText = ToUtf8XmlString(xml); + xmlAsText = xmlAsText.Replace("fcpxml[]", "fcpxml"); + xmlAsText = xmlAsText.Replace("fcpxml []", "fcpxml"); + return xmlAsText; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + FrameRate = Configuration.Settings.General.CurrentFrameRate; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + var xml = new XmlDocument(); + xml.XmlResolver = null; + try + { + xml.LoadXml(sb.ToString().Trim()); + + foreach (XmlNode node in xml.SelectNodes("fcpxml/project/sequence/spine/gap")) + { + try + { + foreach (XmlNode title in node.SelectNodes("title")) + { + var textNodes = title.SelectNodes("text"); + if (textNodes != null && textNodes.Count > 0) + { + Paragraph p = new Paragraph(); + p.StartTime = DecodeTime(title.Attributes["offset"]); + p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + DecodeTime(title.Attributes["duration"]).TotalMilliseconds; + var text = new StringBuilder(); + foreach (XmlNode textNode in textNodes) + { + text.AppendLine(textNode.InnerText); + } + p.Text = text.ToString().Trim(); + subtitle.Paragraphs.Add(p); + } + } + } + catch + { + _errorCount++; + } + } + subtitle.Renumber(); + } + catch + { + _errorCount = 1; + return; + } + } + + private static TimeCode DecodeTime(XmlAttribute duration) + { + // 220220/60000s + if (duration != null) + { + var arr = duration.Value.TrimEnd('s').Split('/'); + if (arr.Length == 2) + { + return TimeCode.FromSeconds(long.Parse(arr[0]) / double.Parse(arr[1])); + } + else if (arr.Length == 1) + { + return TimeCode.FromSeconds(float.Parse(arr[0])); + } + } + return new TimeCode(0, 0, 0, 0); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/FlashXml.cs b/libse/SubtitleFormats/FlashXml.cs new file mode 100644 index 000000000..3dd64e4d2 --- /dev/null +++ b/libse/SubtitleFormats/FlashXml.cs @@ -0,0 +1,187 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + + // + // + //
+ //

This is fully skinnable through XML
using external images for each button]]>

+ //

You can put in any order or enable/disable
the control buttons]]>

+ //

Test below some of the customizable
properties this player has]]>

+ //

Many other properties related to fonts, sizes, colors
and list properties are in style.css file]]>

+ //
+ //
+ public class FlashXml : SubtitleFormat + { + public override string Extension + { + get { return ".xml"; } + } + + public override string Name + { + get { return "Flash Xml"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + string xmlAsString = sb.ToString().Trim(); + if ((xmlAsString.Contains("") || xmlAsString.Contains(""))) + { + var xml = new XmlDocument { XmlResolver = null }; + try + { + xml.LoadXml(xmlAsString); + var paragraphs = xml.DocumentElement.SelectNodes("div/p"); + return paragraphs != null && paragraphs.Count > 0 && xml.DocumentElement.Name == "tt"; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + } + } + return false; + } + + private static string ConvertToTimeString(TimeCode time) + { + return string.Format("{0:00}:{1:00}:{2:00}.{3:00}", time.Hours, time.Minutes, time.Seconds, time.Milliseconds); + } + + public override string ToText(Subtitle subtitle, string title) + { + string xmlStructure = + "" + Environment.NewLine + + "" + Environment.NewLine + + "
" + Environment.NewLine + + "
" + Environment.NewLine + + "
"; + + var xml = new XmlDocument(); + xml.LoadXml(xmlStructure); + XmlNode div = xml.DocumentElement.SelectSingleNode("div"); + foreach (Paragraph p in subtitle.Paragraphs) + { + XmlNode paragraph = xml.CreateElement("p"); + string text = HtmlUtil.RemoveHtmlTags(p.Text, true); + + paragraph.InnerText = text; + paragraph.InnerXml = "" + paragraph.InnerXml.Replace(Environment.NewLine, "
") + "]]>"; + + XmlAttribute start = xml.CreateAttribute("begin"); + start.InnerText = ConvertToTimeString(p.StartTime); + paragraph.Attributes.Append(start); + + XmlAttribute end = xml.CreateAttribute("end"); + end.InnerText = ConvertToTimeString(p.EndTime); + paragraph.Attributes.Append(end); + + div.AppendChild(paragraph); + } + + return ToUtf8XmlString(xml); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + double startSeconds = 0; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + var xml = new XmlDocument { XmlResolver = null }; + xml.LoadXml(sb.ToString().Trim()); + + var pText = new StringBuilder(); + foreach (XmlNode node in xml.DocumentElement.SelectNodes("div/p")) + { + try + { + foreach (XmlNode innerNode in node.ChildNodes) + { + switch (innerNode.Name) + { + case "br": + pText.AppendLine(); + break; + default: + pText.Append(innerNode.InnerText.Trim()); + break; + } + } + + var start = string.Empty; + if (node.Attributes["begin"] != null) + { + start = node.Attributes["begin"].InnerText; + } + + var end = string.Empty; + if (node.Attributes["end"] != null) + { + end = node.Attributes["end"].InnerText; + } + + var dur = string.Empty; + if (node.Attributes["dur"] != null) + { + dur = node.Attributes["dur"].InnerText; + } + + TimeCode startCode = TimeCode.FromSeconds(startSeconds); + if (start.Length > 0) + { + startCode = GetTimeCode(start); + } + + TimeCode endCode; + if (end.Length > 0) + { + endCode = GetTimeCode(end); + } + else if (dur.Length > 0) + { + endCode = new TimeCode(GetTimeCode(dur).TotalMilliseconds + startCode.TotalMilliseconds); + } + else + { + endCode = new TimeCode(startCode.TotalMilliseconds + 3000); + } + startSeconds = endCode.TotalSeconds; + + subtitle.Paragraphs.Add(new Paragraph(startCode, endCode, pText.ToString().Replace("", string.Empty).Replace("", string.Empty))); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + _errorCount++; + } + pText.Clear(); + } + subtitle.Renumber(); + } + + private static TimeCode GetTimeCode(string s) + { + if (s.EndsWith('s')) + { + s = s.TrimEnd('s'); + return TimeCode.FromSeconds(double.Parse(s)); + } + string[] parts = s.Split(new char[] { ':', '.', ',' }); + return new TimeCode(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), int.Parse(parts[3])); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/Footage.cs b/libse/SubtitleFormats/Footage.cs new file mode 100644 index 000000000..7f52bb8f4 --- /dev/null +++ b/libse/SubtitleFormats/Footage.cs @@ -0,0 +1,168 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class Footage : SubtitleFormat + { + private static readonly Regex RegexTimeCode = new Regex(@"^\s*\d+,\d\d$", RegexOptions.Compiled); + + private enum ExpectingLine + { + Number, + TimeStart, + TimeEnd, + Text + } + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Footage"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var asc = new TimeLineFootageAscii(); + if (fileName != null && asc.IsMine(null, fileName)) + return false; + + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + //1. + // 66,13 + // 70,00 + //#Tā nu es sapazinos + //#И так я познакомился + + //2. + // 71,14 + // 78,10 + //#ar dakteri Henriju Gūsu. + //#с доктором Генри Гусом. + + const string paragraphWriteFormat = "{4}.{3}{0}{3}{1}{3}{2}{3}"; + var sb = new StringBuilder(); + int count = 0; + foreach (Paragraph p in subtitle.Paragraphs) + { + count++; + string text = HtmlUtil.RemoveHtmlTags(p.Text); + if (p.Text.StartsWith("") && p.Text.EndsWith("")) + text = "#" + text; + sb.AppendLine(string.Format(paragraphWriteFormat, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), text, Environment.NewLine, count)); + } + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + Paragraph paragraph = null; + ExpectingLine expecting = ExpectingLine.Number; + _errorCount = 0; + + subtitle.Paragraphs.Clear(); + foreach (string line in lines) + { + if (line.EndsWith('.') && Utilities.IsInteger(line.TrimEnd('.'))) + { + if (paragraph != null && !string.IsNullOrEmpty(paragraph.Text)) + subtitle.Paragraphs.Add(paragraph); + paragraph = new Paragraph(); + expecting = ExpectingLine.TimeStart; + } + else if (paragraph != null && expecting == ExpectingLine.TimeStart && RegexTimeCode.IsMatch(line)) + { + string[] parts = line.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 2) + { + try + { + var tc = DecodeTimeCode(parts); + paragraph.StartTime = tc; + expecting = ExpectingLine.TimeEnd; + } + catch + { + _errorCount++; + expecting = ExpectingLine.Number; + } + } + } + else if (paragraph != null && expecting == ExpectingLine.TimeEnd && RegexTimeCode.IsMatch(line)) + { + string[] parts = line.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 2) + { + try + { + var tc = DecodeTimeCode(parts); + paragraph.EndTime = tc; + expecting = ExpectingLine.Text; + } + catch + { + _errorCount++; + expecting = ExpectingLine.Number; + } + } + } + else + { + if (paragraph != null && expecting == ExpectingLine.Text) + { + if (line.Length > 0) + { + string s = line.Trim(); + if (s.StartsWith('#')) + s = "" + s.Remove(0, 1) + ""; + paragraph.Text = (paragraph.Text + Environment.NewLine + s).Trim(); + paragraph.Text = paragraph.Text.Replace("
" + Environment.NewLine + "", Environment.NewLine); + if (paragraph.Text.Length > 2000) + { + _errorCount += 100; + return; + } + } + } + } + } + if (paragraph != null && !string.IsNullOrEmpty(paragraph.Text)) + subtitle.Paragraphs.Add(paragraph); + + subtitle.Renumber(); + } + + private static string EncodeTimeCode(TimeCode time) + { + int frames = MillisecondsToFrames(time.TotalMilliseconds); + int footage = frames / 16; + int rest = (int)((frames % 16) / 16.0 * 24.0); + return string.Format("{0:00},{1:00}", footage, rest).PadLeft(8); + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + int frames16 = int.Parse(parts[0]); + int frames = int.Parse(parts[1]); + return new TimeCode(0, 0, 0, FramesToMilliseconds(16 * frames16 + frames)); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/GpacTtxt.cs b/libse/SubtitleFormats/GpacTtxt.cs new file mode 100644 index 000000000..ea42a1a6d --- /dev/null +++ b/libse/SubtitleFormats/GpacTtxt.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class GpacTtxt : SubtitleFormat + { + public override string Extension + { + get { return ".ttxt"; } + } + + public override string Name + { + get { return "GPAC TTXT"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > 0; + } + + public override string ToText(Subtitle subtitle, string title) + { + string xmlStructure = + "" + Environment.NewLine + + "" + Environment.NewLine + + "" + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + ""; + + string xmlTrackStructure = + "" + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " 00:03:57:16" + Environment.NewLine + + ""; + + if (string.IsNullOrEmpty(title)) + title = "Subtitle Edit subtitle"; + + var xml = new XmlDocument(); + xml.XmlResolver = null; + xml.LoadXml(xmlStructure); + // TODO: Set variables... + XmlNode trackNode = xml.DocumentElement.SelectSingleNode("TrackList/Track/StItemList"); + + int number = 1; + foreach (Paragraph p in subtitle.Paragraphs) + { + // starttime + text + XmlNode stItem = xml.CreateElement("StItem"); + stItem.InnerXml = xmlTrackStructure; + + XmlAttribute memo = xml.CreateAttribute("Memo"); + memo.InnerText = string.Empty; + stItem.Attributes.Append(memo); + + XmlAttribute tc = xml.CreateAttribute("TC"); + tc.InnerText = p.StartTime.TotalMilliseconds.ToString(); + stItem.Attributes.Append(tc); + + XmlAttribute row = xml.CreateAttribute("Row"); + row.InnerText = number.ToString(); + stItem.Attributes.Append(row); + + XmlNodeList list = stItem.SelectNodes("StTextList/StText"); + list[0].InnerText = p.Text; + list[2].InnerText = p.StartTime.ToHHMMSSFF(); + trackNode.AppendChild(stItem); + number++; + + // endtime + stItem = xml.CreateElement("StItem"); + stItem.InnerXml = xmlTrackStructure; + + memo = xml.CreateAttribute("Memo"); + memo.InnerText = string.Empty; + stItem.Attributes.Append(memo); + + tc = xml.CreateAttribute("TC"); + tc.InnerText = p.EndTime.TotalMilliseconds.ToString(); + stItem.Attributes.Append(tc); + + row = xml.CreateAttribute("Row"); + row.InnerText = number.ToString(); + stItem.Attributes.Append(row); + + list = stItem.SelectNodes("StTextList/StText"); + list[0].InnerText = string.Empty; + list[2].InnerText = p.EndTime.ToString(); + trackNode.AppendChild(stItem); + number++; + } + + return ToUtf8XmlString(xml); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + if (!sb.ToString().Contains(" 1) + { + text = (list[0].InnerText + Environment.NewLine + list[1].InnerText).Trim(); + } + else if (list.Count == 1) + { + text = list[0].InnerText.Trim(); + } + + p.Text = text; + subtitle.Paragraphs.Add(p); + } + catch + { + _errorCount++; + } + } + subtitle.Renumber(); + } + catch + { + _errorCount = 1; + return; + } + + int i = 0; + foreach (Paragraph p in subtitle.Paragraphs) + { + i++; + var next = subtitle.GetParagraphOrDefault(i); + if (next != null) + p.EndTime.TotalMilliseconds = next.StartTime.TotalMilliseconds; + } + subtitle.RemoveEmptyLines(); + } + + } +} diff --git a/libse/SubtitleFormats/ItunesTimedText.cs b/libse/SubtitleFormats/ItunesTimedText.cs new file mode 100644 index 000000000..81cfcc057 --- /dev/null +++ b/libse/SubtitleFormats/ItunesTimedText.cs @@ -0,0 +1,320 @@ +using System; +using System.Collections.Generic; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + /// + /// Crappy format... should always be saved as UTF-8 without BOM (hacked Main.cs) and
tags should be oldstyle
+ ///
+ public class ItunesTimedText : TimedText10 + { + public override string Extension + { + get { return ".itt"; } + } + + public override string Name + { + get { return "iTunes Timed Text"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + if (fileName != null && !fileName.EndsWith(Extension, StringComparison.OrdinalIgnoreCase)) + return false; + + return base.IsMine(lines, fileName); + } + + public override string ToText(Subtitle subtitle, string title) + { + XmlNode styleHead = null; + if (subtitle.Header != null) + { + try + { + var x = new XmlDocument(); + x.LoadXml(subtitle.Header); + var xnsmgr = new XmlNamespaceManager(x.NameTable); + xnsmgr.AddNamespace("ttml", "http://www.w3.org/ns/ttml"); + styleHead = x.DocumentElement.SelectSingleNode("ttml:head", xnsmgr); + } + catch + { + styleHead = null; + } + if (styleHead == null && (subtitle.Header.Contains("[V4+ Styles]") || subtitle.Header.Contains("[V4 Styles]"))) + { + var x = new XmlDocument(); + x.LoadXml(new ItunesTimedText().ToText(new Subtitle(), "tt")); // load default xml + var xnsmgr = new XmlNamespaceManager(x.NameTable); + xnsmgr.AddNamespace("ttml", "http://www.w3.org/ns/ttml"); + styleHead = x.DocumentElement.SelectSingleNode("ttml:head", xnsmgr); + styleHead.SelectSingleNode("ttml:styling", xnsmgr).RemoveAll(); + foreach (string styleName in AdvancedSubStationAlpha.GetStylesFromHeader(subtitle.Header)) + { + try + { + var ssaStyle = AdvancedSubStationAlpha.GetSsaStyle(styleName, subtitle.Header); + if (ssaStyle != null) + { + string fontStyle = "normal"; + if (ssaStyle.Italic) + fontStyle = "italic"; + string fontWeight = "normal"; + if (ssaStyle.Bold) + fontWeight = "bold"; + AddStyleToXml(x, styleHead, xnsmgr, ssaStyle.Name, ssaStyle.FontName, fontWeight, fontStyle, Utilities.ColorToHex(ssaStyle.Primary), ssaStyle.FontSize.ToString()); + } + } + catch + { + } + } + subtitle.Header = x.OuterXml; // save new xml with styles in header + } + } + + var xml = new XmlDocument(); + xml.XmlResolver = null; + var nsmgr = new XmlNamespaceManager(xml.NameTable); + nsmgr.AddNamespace("ttml", "http://www.w3.org/ns/ttml"); + nsmgr.AddNamespace("ttp", "http://www.w3.org/ns/10/ttml#parameter"); + nsmgr.AddNamespace("tts", "http://www.w3.org/ns/10/ttml#style"); + nsmgr.AddNamespace("ttm", "http://www.w3.org/ns/10/ttml#metadata"); + + string frameRate = "24"; + string frameRateMultiplier = "999 1000"; + if (Math.Abs(Configuration.Settings.General.CurrentFrameRate - 30.0) < 0.2) + { + frameRate = "30"; + } + else if (Math.Abs(Configuration.Settings.General.CurrentFrameRate - 25.0) < 0.01) + { + frameRate = "25"; + frameRateMultiplier = "1 1"; + } + + const string language = "en-US"; + string xmlStructure = "" + Environment.NewLine + + "" + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " + + +<-- Open play menu, choose Captions and Subtiles, On if available --> +<-- Open tools menu, Security, Show local captions when present -->"; + + bool useExtra = false; + if (!string.IsNullOrEmpty(subtitle.Header) && subtitle.Header.StartsWith(" + +_TITLE_ + + Metrics {time:ms;} + Spec {MSFT:1.0;} + +" + subtitle.Header.Trim() + @" + + +<-- Open play menu, choose Captions and Subtiles, On if available --> +<-- Open tools menu, Security, Show local captions when present -->"; + } + + // Example text (start numbers are milliseconds) + //

Let's go! + //


+ + string paragraphWriteFormat = @"

{2}" + Environment.NewLine + + @"

 "; + string paragraphWriteFormatOpen = @"

{1}"; + if (Name == new SamiModern().Name) + { + paragraphWriteFormat = "

{2}

" + Environment.NewLine + + "

 

"; + paragraphWriteFormatOpen = "

{1}

"; + } + else if (Name == new SamiYouTube().Name) + { + paragraphWriteFormat = "

{2}

" + Environment.NewLine + + "

"; + paragraphWriteFormatOpen = "

{1}

"; + } + + int count = 1; + var sb = new StringBuilder(); + sb.AppendLine(header.Replace("_TITLE_", title).Replace("_LANGUAGE-STYLE_", languageStyle)); + var totalLine = new StringBuilder(); + var partialLine = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + Paragraph next = subtitle.GetParagraphOrDefault(count); + string text = p.Text; + + if (text.Contains('<') && text.Contains('>')) + { + bool tagOn = false; + for (int i = 0; i < text.Length; i++) + { + string t = text.Substring(i); + if (t.StartsWith('<') && + (t.StartsWith("') && tagOn) + { + tagOn = false; + totalLine.Append('>'); + } + else if (!tagOn) + { + partialLine.Append(text[i]); + } + else + { + totalLine.Append(text[i]); + } + } + + totalLine.Append(EncodeText(partialLine.ToString())); + text = totalLine.ToString(); + totalLine.Clear(); + partialLine.Clear(); + } + else + { + text = EncodeText(text); + } + + if (Name == new SamiModern().Name) + text = text.Replace(Environment.NewLine, "
"); + else + text = text.Replace(Environment.NewLine, "
"); + + string currentClass = languageTag; + if (useExtra && !string.IsNullOrEmpty(p.Extra)) + currentClass = p.Extra; + + var startMs = (long)(Math.Round(p.StartTime.TotalMilliseconds)); + var endMs = (long)(Math.Round(p.EndTime.TotalMilliseconds)); + if (next != null && Math.Abs(((long)Math.Round(next.StartTime.TotalMilliseconds)) - endMs) < 1) + sb.AppendLine(string.Format(paragraphWriteFormatOpen, startMs, text, currentClass)); + else + sb.AppendLine(string.Format(paragraphWriteFormat, startMs, endMs, text, currentClass)); + count++; + } + sb.AppendLine(""); + sb.AppendLine(""); + return sb.ToString().Trim(); + } + + private static string EncodeText(string text) + { + switch (Configuration.Settings.SubtitleSettings.SamiHtmlEncodeMode) + { + case 1: + return WebUtility.HtmlEncode(text); + case 2: + return HtmlUtil.EncodeNamed(text); + case 3: + return HtmlUtil.EncodeNumeric(text); + } + return text; + } + + public static List GetStylesFromHeader(string header) + { + var list = new List(); + if (!string.IsNullOrEmpty(header) && header.StartsWith(" 2) + { + string name = s.Substring(1, s.IndexOf(' ') - 1); + list.Add(name); + } + } + } + else + { + list.Add("ENUSCC"); + } + return list; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + var sb = new StringBuilder(); + foreach (string l in lines) + sb.AppendLine(l.Replace(" 0) + { + int styleEnd = allInputLower.IndexOf("", StringComparison.Ordinal); + if (styleEnd > 0) + { + subtitle.Header = allInput.Substring(styleStart, styleEnd - styleStart + 8); + } + } + + const string syncTag = "') + index++; + + int syncEndPos = allInputLower.IndexOf(syncTag, index, StringComparison.Ordinal); + int syncEndPosEnc = allInputLower.IndexOf(syncTagEnc, index, StringComparison.Ordinal); + if ((syncStartPosEnc >= 0 && syncStartPosEnc < syncStartPos) || syncEndPos == -1) + syncEndPos = syncEndPosEnc; + + string text; + if (syncEndPos >= 0) + text = allInput.Substring(index, syncEndPos - index); + else + text = allInput.Substring(index); + + string textToLower = text.ToLower(); + if (textToLower.Contains(" class=")) + { + var className = new StringBuilder(); + int startClass = textToLower.IndexOf(" class=", StringComparison.Ordinal); + int indexClass = startClass + 7; + while (indexClass < textToLower.Length && (Utilities.LowercaseLettersWithNumbers + @"'""").Contains(textToLower[indexClass])) + { + className.Append(text[indexClass]); + indexClass++; + } + p.Extra = className.ToString().Trim(' ', '\'', '"'); + } + + if (text.Contains("ID=\"Source\"") || text.Contains("ID=Source")) + { + int sourceIndex = text.IndexOf("ID=\"Source\"", StringComparison.Ordinal); + if (sourceIndex < 0) + sourceIndex = text.IndexOf("ID=Source", StringComparison.Ordinal); + int st = sourceIndex - 1; + while (st > 0 && text.Substring(st, 2).ToUpper() != " 0) + { + text = text.Substring(0, st) + text.Substring(sourceIndex); + } + int et = st; + while (et < text.Length - 5 && text.Substring(et, 3).ToUpper() != "

" && text.Substring(et, 4).ToUpper() != "

") + { + et++; + } + text = text.Substring(0, st) + text.Substring(et); + } + text = text.Replace(Environment.NewLine, " "); + text = text.Replace(" ", " "); + + text = text.TrimEnd(); + text = Regex.Replace(text, @"
", Environment.NewLine, RegexOptions.IgnoreCase); + + while (text.Contains(" ")) + text = text.Replace(" ", " "); + text = text.Replace("", string.Empty).Replace("", string.Empty).TrimEnd(); + + int endSyncPos = text.ToUpper().IndexOf("
", StringComparison.Ordinal); + if (text.IndexOf('>') > 0 && (text.IndexOf('>') < endSyncPos || endSyncPos == -1)) + text = text.Remove(0, text.IndexOf('>') + 1); + text = text.TrimEnd(); + + if (text.EndsWith("
", StringComparison.OrdinalIgnoreCase)) + text = text.Substring(0, text.Length - 7).TrimEnd(); + + if (text.EndsWith("

", StringComparison.Ordinal) || text.EndsWith("

", StringComparison.Ordinal)) + text = text.Substring(0, text.Length - 4).TrimEnd(); + + text = text.Replace(" ", " ").Replace("&NBSP;", " "); + + if (text.Contains("")) + text += ""; + if (text.StartsWith("") && !text.Contains("")) + text += "
"; + + if (text.Contains('<') && text.Contains('>')) + { + var total = new StringBuilder(); + var partial = new StringBuilder(); + bool tagOn = false; + for (int i = 0; i < text.Length && i < 999; i++) + { + string tmp = text.Substring(i); + if (tmp.StartsWith('<') && + (tmp.StartsWith("') && tagOn) + { + tagOn = false; + total.Append('>'); + } + else if (!tagOn) + { + partial.Append(text[i]); + } + else + { + total.Append(text[i]); + } + } + total.Append(WebUtility.HtmlDecode(partial.ToString())); + text = total.ToString(); + } + else + { + text = WebUtility.HtmlDecode(text); + } + + var cleanText = text.FixExtraSpaces(); + cleanText = cleanText.Trim(); + + if (!string.IsNullOrEmpty(p.Text) && !string.IsNullOrEmpty(millisecAsString)) + { + p.EndTime = new TimeCode(long.Parse(millisecAsString)); + subtitle.Paragraphs.Add(p); + p = new Paragraph(); + } + + p.Text = cleanText; + long l; + if (long.TryParse(millisecAsString, out l)) + p.StartTime = new TimeCode(l); + + if (syncEndPos <= 0) + { + syncStartPos = -1; + } + else + { + syncStartPos = allInputLower.IndexOf(syncTag, syncEndPos, StringComparison.Ordinal); + index = syncStartPos + syncTag.Length; + + syncStartPosEnc = allInputLower.IndexOf(syncTagEnc, syncEndPos, StringComparison.Ordinal); + if ((syncStartPosEnc >= 0 && syncStartPosEnc < syncStartPos) || syncStartPos == -1) + { + syncStartPos = syncStartPosEnc; + index = syncStartPosEnc + syncTagEnc.Length; + } + } + } + if (!string.IsNullOrEmpty(p.Text) && !subtitle.Paragraphs.Contains(p)) + { + p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + Utilities.GetOptimalDisplayMilliseconds(p.Text); + subtitle.Paragraphs.Add(p); + } + subtitle.Renumber(); + + if (subtitle.Paragraphs.Count > 0 && + (subtitle.Paragraphs[subtitle.Paragraphs.Count - 1].Text.ToUpper().Trim() == "" || + subtitle.Paragraphs[subtitle.Paragraphs.Count - 1].Text.ToUpper().Trim() == "")) + subtitle.Paragraphs.RemoveAt(subtitle.Paragraphs.Count - 1); + + foreach (Paragraph p2 in subtitle.Paragraphs) + { + p2.Text = p2.Text.Replace('\u00A0', ' '); // non-breaking space to normal space + } + } + + public override bool HasStyleSupport + { + get { return true; } + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/SamiModern.cs b/libse/SubtitleFormats/SamiModern.cs new file mode 100644 index 000000000..b3108b758 --- /dev/null +++ b/libse/SubtitleFormats/SamiModern.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class SamiModern : Sami + { + public override string Name + { + get { return "SAMI modern"; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/SamiYouTube.cs b/libse/SubtitleFormats/SamiYouTube.cs new file mode 100644 index 000000000..17f8c877f --- /dev/null +++ b/libse/SubtitleFormats/SamiYouTube.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class SamiYouTube : Sami + { + public override string Name + { + get { return "SAMI YouTube"; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/SatBoxPng.cs b/libse/SubtitleFormats/SatBoxPng.cs new file mode 100644 index 000000000..c98b3c609 --- /dev/null +++ b/libse/SubtitleFormats/SatBoxPng.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + /// + /// http://forum.videohelp.com/threads/365786-Converting-Subtitles-%28XML-PNG%29-to-idx-sub + /// + public class SatBoxPng : SubtitleFormat + { + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "SatBox png"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + return "Not implemented"; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + // + Paragraph p = null; + subtitle.Paragraphs.Clear(); + _errorCount = 0; + if (string.IsNullOrEmpty(fileName)) + return; + string path = Path.GetDirectoryName(fileName); + foreach (string line in lines) + { + if (line.Contains(" s=\"") && line.Contains(" e=\"") && line.Contains(" i=\"") && line.Contains(".png") && (line.Contains(" 0 && line.Length > start + 4) + { + int end = line.IndexOf('"', start + 3); + if (end > 0 && line.Length > end + 3) + { + string value = line.Substring(start + 3, end - start - 3); + return value; + } + } + return string.Empty; + } + + private static TimeCode DecodeTimeCode(string s) + { + return TimeCode.FromSeconds(double.Parse(s, CultureInfo.InvariantCulture)); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/Scenarist.cs b/libse/SubtitleFormats/Scenarist.cs new file mode 100644 index 000000000..6392dd2ac --- /dev/null +++ b/libse/SubtitleFormats/Scenarist.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class Scenarist : SubtitleFormat + { + private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d\d\d\t\d\d:\d\d:\d\d:\d\d\t\d\d:\d\d:\d\d:\d\d\t", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public const string NameOfFormat = "Scenarist"; + + public override string Name + { + get { return NameOfFormat; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + + var sb = new StringBuilder(); + foreach (string line in lines) + sb.AppendLine(line); + + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + int index = 0; + foreach (Paragraph p in subtitle.Paragraphs) + { + //0003 00:00:28:16 00:00:31:04 Jeg vil lære jer frygten for HERREN." (newline is \t) + sb.AppendLine(string.Format("{0:0000}\t{1}\t{2}\t{3}", index + 1, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), HtmlUtil.RemoveHtmlTags(p.Text).Replace(Environment.NewLine, "\t"))); + index++; + } + return sb.ToString(); + } + + private static string EncodeTimeCode(TimeCode time) + { + //00:03:15:22 (last is frame) + return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", time.Hours, time.Minutes, time.Seconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + //00:03:15:22 00:03:23:10 This is line one. + //This is line two. + Paragraph p = null; + subtitle.Paragraphs.Clear(); + _errorCount = 0; + foreach (string line in lines) + { + if (RegexTimeCodes.IsMatch(line)) + { + string temp = line.Substring(0, RegexTimeCodes.Match(line).Length); + string start = temp.Substring(5, 11); + string end = temp.Substring(12 + 5, 11); + + string[] startParts = start.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + string[] endParts = end.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + if (startParts.Length == 4 && endParts.Length == 4) + { + string text = line.Remove(0, RegexTimeCodes.Match(line).Length - 1).Trim(); + if (!text.Contains(Environment.NewLine)) + text = text.Replace("\t", Environment.NewLine); + p = new Paragraph(DecodeTimeCode(startParts), DecodeTimeCode(endParts), text); + subtitle.Paragraphs.Add(p); + } + } + else if (string.IsNullOrWhiteSpace(line)) + { + // skip these lines + } + else if (p != null) + { + _errorCount++; + } + } + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + //00:00:07:12 + string hour = parts[0]; + string minutes = parts[1]; + string seconds = parts[2]; + string frames = parts[3]; + + return new TimeCode(int.Parse(hour), int.Parse(minutes), int.Parse(seconds), FramesToMillisecondsMax999(int.Parse(frames))); + } + + } +} diff --git a/libse/SubtitleFormats/ScenaristClosedCaptions.cs b/libse/SubtitleFormats/ScenaristClosedCaptions.cs new file mode 100644 index 000000000..7887c78d0 --- /dev/null +++ b/libse/SubtitleFormats/ScenaristClosedCaptions.cs @@ -0,0 +1,1790 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Globalization; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + + /// + /// http://www.theneitherworld.com/mcpoodle/SCC_TOOLS/DOCS/SCC_FORMAT.HTML + /// § 15.119 47 CFR Ch. I (10–1–10 Edition) (pdf) + /// Maximum four lines + max 32 characters on each line + /// + public class ScenaristClosedCaptions : SubtitleFormat + { + public class SccPositionAndStyle + { + public Color ForeColor { get; set; } + public FontStyle Style { get; set; } + public int X { get; set; } + public int Y { get; set; } + + public SccPositionAndStyle(Color color, FontStyle style, int y, int x) + { + ForeColor = color; + Style = style; + X = x; + Y = y; + } + } + + //00:01:00:29 9420 9420 94ae 94ae 94d0 94d0 4920 f761 7320 ... semi colon (instead of colon) before frame number is used to indicate drop frame + private const string TimeCodeRegEx = @"^\d+:\d\d:\d\d[:,]\d\d\t"; + private static readonly Regex Regex = new Regex(TimeCodeRegEx, RegexOptions.Compiled); + protected virtual Regex RegexTimeCodes { get { return Regex; } } + protected bool DropFrame = false; + + private static readonly List Letters = new List + { + " ", + "!", + "\"", + "#", + "$", + "%", + "&", + "'", + "(", + ")", + "á", + "+", + ",", + "-", + ".", + "/", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + ":", + ";", + "<", + "=", + ">", + "?", + "@", + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + "[", + "é", + "]", + "í", + "ó", + "ú", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "ç", + "y", + "z", + "", + "", + "Ñ", + "ñ", // "fe" + "■", // "7f" + "ç", // "7b" + "c", // 63 + "e", // 65 + "f", // 66 + "i", // 69 + "j", // 6a + "l", // 6c + "n", // 6e + "o", // 6f + "q", // 71 + "r", // 72 + "t", // 74 + "w", // 77 + "x", // 78 + "", + "°", + "½", + "", + "", + "", + "£", + "♪", + "à", + "", + "è", + "â", + "ê", + "î", + "ô", + "û", + "Á", + "É", + "Ó", + "Ú", + "Ü", + "ü", + "'", + "i", + "*", + "'", + "-", + "", + "", + "\"", + "\"", + "", + "À", + "Â", + "", + "È", + "Ê", + "Ë", + "ë", + "Î", + "Ï", + "ï", + "ô", + "Ù", + "ù", + "Û", + "", + "", + "Ã", + "ã", + "Í", + "Ì", + "ì", + "Ò", + "ò", + "Õ", + "õ", + "{", + "}", + "\\", + "^", + "_", + "|", + "~", + "Ä", + "ä", + "Ö", + "ö", + "", + "", + "", + "|", + "Å", + "å", + "Ø", + "ø", + "", + "", + "", + "", + "", //9420=RCL, Resume Caption Loading + "", //94ae=Clear Buffer + "", //942c=Clear Caption + "", //8080=Wait One Frame + "", //942f=Display Caption + "", //9440=? + "", //9452 + "", //9454 + "", //9470=? + "", //94d0=? + "", //94d6=? + "", //942f=End of Caption + "", //94f2=? + "", //94f4=? + " ", //9723=? + " ", //97a1=? + " ", //97a2=? + "", //1370=? + "", //13e0=? + "", //13f2=? + "", //136e=? + "", //94ce=? + "", //2c2f=? + + "®", //9130 + "°", //9131 + "½", //9132 + "¿", //9133 + "TM",//9134 + "¢", //9135 + "£", //9136 + "♪", //9137 + "à", //9138 + " ", //9138 + "è", //913a + "â", //913b + "ê", //913c + "î", //913d + "ô", //913e + "û", //913f + + "®", //1130 + "°", //1131 + "½", //1132 + "¿", //1133 + "TM",//1134 + "¢", //1135 + "£", //1136 + "♪", //1137 + "à", //1138 + " ", //1138 + "è", //113a + "â", //113b + "ê", //113c + "î", //113d + "ô", //113e + "û", //113f + "¡", //a180 92a7 92a7 + "¿", //91b3 91b3 + + }; + + private static readonly List LetterCodes = new List + { + "20", // " ", + "a1", // "!", + "a2", // "\"", + "23", // "#", + "a4", // "$", + "25", // "%", + "26", // "&", + "a7", // "'", + "a8", // "(", + "29", // ")", + "2a", // "á", + "ab", // "+", + "2c", // ",", + "ad", // "-", + "ae", // ".", + "2f", // "/", + "b0", // "0", + "31", // "1", + "32", // "2", + "b3", // "3", + "34", // "4", + "b5", // "5", + "b6", // "6", + "37", // "7", + "38", // "8", + "b9", // "9", + "ba", // ":", + "3b", // ";", + "bc", // "<", + "3d", // "=", + "3e", // ">", + "bf", // "?", + "40", // "@", + "c1", // "A", + "c2", // "B", + "43", // "C", + "c4", // "D", + "45", // "E", + "46", // "F", + "c7", // "G", + "c8", // "H", + "49", // "I", + "4a", // "J", + "cb", // "K", + "4c", // "L", + "cd", // "M", + "ce", // "N", + "4f", // "O", + "d0", // "P", + "51", // "Q", + "52", // "R", + "d3", // "S", + "54", // "T", + "d5", // "U", + "d6", // "V", + "57", // "W", + "58", // "X", + "d9", // "Y", + "da", // "Z", + "5b", // "[", + "dc", // "é", + "5d", // "]", + "5e", // "í", + "df", // "ó", + "e0", // "ú", + "61", // "a", + "62", // "b", + "e3", // "c", + "64", // "d", + "e5", // "e", + "e6", // "f", + "67", // "g", + "68", // "h", + "e9", // "i", + "ea", // "j", + "6b", // "k", + "ec", // "l", + "6d", // "m", + "6e", // "n", + "ef", // "o", + "70", // "p", + "f1", // "q", + "f2", // "r", + "73", // "s", + "f4", // "t", + "75", // "u", + "76", // "v", + "f7", // "w", + "f8", // "x", + "fb", // "ç", + "79", // "y", + "7a", // "z", + "fb", // "", + "7c", // "÷", + "fd", // "Ñ", + "fe", // "ñ", + "7f", // "■", + "7b", // "ç", + "63", // "c" + "65", // "e" + "66", // "f" + "69", // "i" + "6a", // "j" + "6c", // "l" + "6e", // "n" + "6f", // "o" + "71", // "q" + "72", // "r" + "74", // "t" + "77", // "w" + "78", // "x" + "91b0", // "", + "9131", // "°", + "9132", // "½", + "91b3", // "", + "9134", // "", + "91b5", // "", + "91b6", // "£", + "9137", // "♪", + "9138", // "à", + "91b9", // "", + "91ba", // "è", + "913b", // "â", + "91bc", // "ê", + "913d", // "î", + "913e", // "ô", + "91bf", // "û", + "9220", // "Á", + "92a1", // "É", + "92a2", // "Ó", + "9223", // "Ú", + "92a4", // "Ü", + "9225", // "ü", + "9226", // "'", + "92a7", // "i", + "92a8", // "*", + "9229", // "'", + "922a", // "-", + "92ab", // "", + "922c", // "", + "92ad", // "\"", + "92ae", // "\"", + "922f", // "", + "92b0", // "À", + "9231", // "Â", + "9232", // "", + "92b3", // "È", + "9234", // "Ê", + "92b5", // "Ë", + "92b6", // "ë", + "9237", // "Î", + "9238", // "Ï", + "92b9", // "ï", + "92b3", // "ô", + "923b", // "Ù", + "92b3", // "ù", + "923d", // "Û", + "923e", // "", + "92bf", // "", + "1320", // "Ã", + "13a1", // "ã", + "13a2", // "Í", + "1323", // "Ì", + "13a4", // "ì", + "1325", // "Ò", + "1326", // "ò", + "13a7", // "Õ", + "13a8", // "õ", + "1329", // "{", + "132a", // "}", + "13ab", // "\\", + "132c", // "^", + "13ad", // "_", + "13ae", // "|", + "132f", // "~", + "13b0", // "Ä", + "1331", // "ä", + "1332", // "Ö", + "13b3", // "ö", + "1334", // "", + "13b5", // "", + "13b6", // "", + "1337", // "|", + "1338", // "Å", + "13b9", // "å", + "13b3", // "Ø", + "133b", // "ø", + "13b3", // "", + "133d", // "", + "133e", // "", + "13bf", // "", + "9420", //9420=RCL, Resume Caption Loading + "94ae", //94ae=Clear Buffer + "942c", //942c=Clear Caption + "8080", //8080=Wait One Frame + "942f", //942f=Display Caption + "9440", //9440=? first sub? + "9452", //? + "9454", //? + "9470", //9470=? + "94d0", //94d0=? + "94d6", //94d6=? + "942f", //942f=End of Caption + "94f2", // + "94f4", // + "9723", // ? + "97a1", // ? + "97a2", // ? + "1370", //1370=? + "13e0", //13e0=? + "13f2", //13f2=? + "136e", //136e=? + "94ce", //94ce=? + "2c2f", //? + + "1130", // ® + "1131", // ° + "1132", // ½ + "1133", // ¿ + "1134", // TM + "1135", // ¢ + "1136", // £ + "1137", // ♪ + "1138", // à + "1138", // transparent space + "113a", // è + "113b", // â + "113c", // ê + "113d", // î + "113e", // ô + "113f", // û + + "9130", // ® + "9131", // ° + "9132", // ½ + "9133", // ¿ + "9134", // TM + "9135", // ¢ + "9136", // £ + "9137", // ♪ + "9138", // à + "9138", // transparent space + "913a", // è + "913b", // â + "913c", // ê + "913d", // î + "913e", // ô + "913f", // û + + "a180 92a7 92a7", // ¡ + "91b3 91b3" // ¡ + + }; + + public override string Extension + { + get { return ".scc"; } + } + + public override string Name + { + get { return "Scenarist Closed Captions"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + private static string FixMax4LinesAndMax32CharsPerLine(string text, string language) + { + var lines = text.Trim().SplitToLines(); + bool allOk = true; + foreach (string line in lines) + { + if (line.Length > 32) + allOk = false; + } + if (lines.Length > 4) + allOk = false; + + if (allOk) + return text; + + text = Utilities.AutoBreakLine(text, 1, 4, language); + lines = text.Trim().SplitToLines(); + allOk = true; + foreach (string line in lines) + { + if (line.Length > 32) + allOk = false; + } + if (lines.Length > 4) + allOk = false; + + if (allOk) + return text; + + text = AutoBreakLineMax4Lines(text, 32); + lines = text.Trim().SplitToLines(); + allOk = true; + foreach (string line in lines) + { + if (line.Length > 32) + allOk = false; + } + if (lines.Length > 4) + allOk = false; + + if (allOk) + return text; + + var sb = new StringBuilder(); + int count = 0; + foreach (string line in lines) + { + if (count < 4) + { + if (line.Length > 32) + sb.AppendLine(line.Substring(0, 32)); + else + sb.AppendLine(line); + } + count++; + } + return sb.ToString().Trim(); + } + + private static int GetLastIndexOfSpace(string s, int endCount) + { + int end = endCount; + if (end >= s.Length) + end = s.Length - 1; + + int i = end; + while (i > 0) + { + if (s[i] == ' ') + return i; + i--; + } + return -1; + } + + private static string AutoBreakLineMax4Lines(string text, int maxLength) + { + string s = text.Replace(Environment.NewLine, " "); + s = s.Replace(" ", " "); + var sb = new StringBuilder(); + int i = GetLastIndexOfSpace(s, maxLength); + if (i > 0) + { + sb.AppendLine(s.Substring(0, i)); + s = s.Remove(0, i).Trim(); + if (s.Length <= maxLength) + i = s.Length; + else + i = GetLastIndexOfSpace(s, maxLength); + if (i > 0) + { + sb.AppendLine(s.Substring(0, i)); + s = s.Remove(0, i).Trim(); + if (s.Length <= maxLength) + i = s.Length; + else + i = GetLastIndexOfSpace(s, maxLength); + if (i > 0) + { + sb.AppendLine(s.Substring(0, i)); + s = s.Remove(0, i).Trim(); + if (s.Length <= maxLength) + i = s.Length; + else + i = GetLastIndexOfSpace(s, maxLength); + if (i > 0) + { + sb.AppendLine(s.Substring(0, i)); + } + else + { + sb.Append(s); + } + } + else + { + sb.Append(s); + } + } + return sb.ToString().Trim(); + } + return text; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + sb.AppendLine("Scenarist_SCC V1.0"); + sb.AppendLine(); + string language = Utilities.AutoDetectGoogleLanguage(subtitle); + for (int i = 0; i < subtitle.Paragraphs.Count; i++) + { + Paragraph p = subtitle.Paragraphs[i]; + sb.AppendLine(string.Format("{0}\t94ae 94ae 9420 9420 {1} 942f 942f", ToTimeCode(p.StartTime.TotalMilliseconds), ToSccText(p.Text, language))); + sb.AppendLine(); + + Paragraph next = subtitle.GetParagraphOrDefault(i + 1); + if (next == null || Math.Abs(next.StartTime.TotalMilliseconds - p.EndTime.TotalMilliseconds) > 100) + { + sb.AppendLine(string.Format("{0}\t942c 942c", ToTimeCode(p.EndTime.TotalMilliseconds))); + sb.AppendLine(); + } + } + return sb.ToString(); + } + + private static string ToSccText(string text, string language) + { + text = FixMax4LinesAndMax32CharsPerLine(text, language); + //text = text.Replace("ã", "aã"); + //text = text.Replace("õ", "oõ"); + + var lines = text.Trim().SplitToLines(); + int italic = 0; + var sb = new StringBuilder(); + int count = 1; + foreach (string line in lines) + { + text = line.Trim(); + if (count > 0) + sb.Append(' '); + sb.Append(GetCenterCodes(text, count, lines.Length)); + count++; + int i = 0; + string code = string.Empty; + if (italic > 0) + { + sb.Append("91ae 91ae "); // italic + } + while (i < text.Length) + { + string s = text.Substring(i, 1); + int index = Letters.IndexOf(s); + string newCode; + if (text.Substring(i).StartsWith("")) + { + newCode = "91ae"; + i += 2; + italic++; + } + else if (text.Substring(i).StartsWith("") && italic > 0) + { + newCode = "9120"; + i += 3; + italic--; + } + else if (index < 0) + newCode = LetterCodes[Letters.IndexOf(" ")]; + else + newCode = LetterCodes[index]; + + if (code.Length == 2 && newCode.Length == 4) + { + code += "80"; + } + + if (code.Length == 4) + { + sb.Append(code + " "); + if (code.StartsWith('9') || code.StartsWith('8')) // control codes must be double + sb.Append(code + " "); + code = string.Empty; + } + + if (code.Length == 2 && newCode.Length == 2) + { + code += newCode; + newCode = string.Empty; + } + + if (newCode.Length == 4 && code.Length == 0) + { + code = newCode; + } + else if (newCode.Length == 2 && code.Length == 0) + { + code = newCode; + } + else if (newCode.Length > 4) + { + if (code.Length == 2) + { + code += "80"; + sb.Append(code + " "); + if (code.StartsWith('9') || code.StartsWith('8')) // control codes must be double + sb.Append(code + " "); + code = string.Empty; + } + else if (code.Length == 4) + { + sb.Append(code + " "); + if (code.StartsWith('9') || code.StartsWith('8')) // control codes must be double + sb.Append(code + " "); + code = string.Empty; + } + sb.Append(newCode.TrimEnd() + " "); + } + + i++; + } + if (code.Length == 2) + code += "80"; + if (code.Length == 4) + sb.Append(code); + } + + return sb.ToString().Trim(); + } + + public static string GetCenterCodes(string text, int lineNumber, int totalLines) + { + int row = 14 - (totalLines - lineNumber); + + var rowCodes = new List { "91", "91", "92", "92", "15", "15", "16", "16", "97", "97", "10", "13", "13", "94", "94" }; + string rowCode = rowCodes[row]; + + int left = (32 - text.Length) / 2; + int columnRest = left % 4; + int column = left - columnRest; + + List columnCodes = null; + switch (column) + { + case 0: columnCodes = new List { "d0", "70", "d0", "70", "d0", "70", "d0", "70", "d0", "70", "d0", "d0", "70", "d0", "70" }; + break; + case 4: columnCodes = new List { "52", "f2", "52", "f2", "52", "f2", "52", "f2", "52", "f2", "52", "52", "f2", "52", "f2" }; + break; + case 8: columnCodes = new List { "54", "f4", "54", "f4", "54", "f4", "54", "f4", "54", "f4", "54", "54", "f4", "54", "f4" }; + break; + case 12: columnCodes = new List { "d6", "76", "d6", "76", "d6", "76", "d6", "76", "d6", "76", "d6", "d6", "76", "d6", "76" }; + break; + case 16: columnCodes = new List { "58", "f8", "58", "f8", "58", "f8", "58", "f8", "58", "f8", "58", "58", "f8", "58", "f8" }; + break; + case 20: columnCodes = new List { "da", "7a", "da", "7a", "da", "7a", "da", "7a", "da", "7a", "da", "da", "7a", "da", "7a" }; + break; + case 24: columnCodes = new List { "dc", "7c", "dc", "7c", "dc", "7c", "dc", "7c", "dc", "7c", "dc", "dc", "7c", "dc", "7c" }; + break; + case 28: columnCodes = new List { "5e", "fe", "5e", "fe", "5e", "fe", "5e", "fe", "5e", "fe", "5e", "5e", "fe", "5e", "fe" }; + break; + } + string code = rowCode + columnCodes[row]; + + if (columnRest == 1) + return code + " " + code + " 97a1 97a1 "; + if (columnRest == 2) + return code + " " + code + " 97a2 97a2 "; + if (columnRest == 3) + return code + " " + code + " 9723 9723 "; + return code + " " + code + " "; + } + + private string ToTimeCode(double totalMilliseconds) + { + TimeSpan ts = TimeSpan.FromMilliseconds(totalMilliseconds); + if (DropFrame) + return string.Format("{0:00}:{1:00}:{2:00};{3:00}", ts.Hours, ts.Minutes, ts.Seconds, MillisecondsToFramesMaxFrameRate(ts.Milliseconds)); + return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", ts.Hours, ts.Minutes, ts.Seconds, MillisecondsToFramesMaxFrameRate(ts.Milliseconds)); + } + + public static SccPositionAndStyle GetColorAndPosition(string code) + { + switch (code.ToLower(CultureInfo.InvariantCulture)) + { + //NO x-coordinate? + case "1140": return new SccPositionAndStyle(Color.White, FontStyle.Regular, 1, 0); + case "1160": return new SccPositionAndStyle(Color.White, FontStyle.Regular, 2, 0); + case "1240": return new SccPositionAndStyle(Color.White, FontStyle.Regular, 3, 0); + case "1260": return new SccPositionAndStyle(Color.White, FontStyle.Regular, 4, 0); + case "1540": return new SccPositionAndStyle(Color.White, FontStyle.Regular, 5, 0); + case "1560": return new SccPositionAndStyle(Color.White, FontStyle.Regular, 6, 0); + case "1640": return new SccPositionAndStyle(Color.White, FontStyle.Regular, 7, 0); + case "1660": return new SccPositionAndStyle(Color.White, FontStyle.Regular, 8, 0); + case "1740": return new SccPositionAndStyle(Color.White, FontStyle.Regular, 9, 0); + case "1760": return new SccPositionAndStyle(Color.White, FontStyle.Regular, 10, 0); + case "1040": return new SccPositionAndStyle(Color.White, FontStyle.Regular, 11, 0); + case "1340": return new SccPositionAndStyle(Color.White, FontStyle.Regular, 12, 0); + case "1360": return new SccPositionAndStyle(Color.White, FontStyle.Regular, 13, 0); + case "1440": return new SccPositionAndStyle(Color.White, FontStyle.Regular, 14, 0); + case "1460": return new SccPositionAndStyle(Color.White, FontStyle.Regular, 15, 0); + + case "1141": return new SccPositionAndStyle(Color.White, FontStyle.Underline, 1, 0); + case "1161": return new SccPositionAndStyle(Color.White, FontStyle.Underline, 2, 0); + case "1241": return new SccPositionAndStyle(Color.White, FontStyle.Underline, 3, 0); + case "1261": return new SccPositionAndStyle(Color.White, FontStyle.Underline, 4, 0); + case "1541": return new SccPositionAndStyle(Color.White, FontStyle.Underline, 5, 0); + case "1561": return new SccPositionAndStyle(Color.White, FontStyle.Underline, 6, 0); + case "1641": return new SccPositionAndStyle(Color.White, FontStyle.Underline, 7, 0); + case "1661": return new SccPositionAndStyle(Color.White, FontStyle.Underline, 8, 0); + case "1741": return new SccPositionAndStyle(Color.White, FontStyle.Underline, 9, 0); + case "1761": return new SccPositionAndStyle(Color.White, FontStyle.Underline, 10, 0); + case "1041": return new SccPositionAndStyle(Color.White, FontStyle.Underline, 11, 0); + case "1341": return new SccPositionAndStyle(Color.White, FontStyle.Underline, 12, 0); + case "1361": return new SccPositionAndStyle(Color.White, FontStyle.Underline, 13, 0); + case "1441": return new SccPositionAndStyle(Color.White, FontStyle.Underline, 14, 0); + case "1461": return new SccPositionAndStyle(Color.White, FontStyle.Underline, 15, 0); + + case "1142": return new SccPositionAndStyle(Color.Green, FontStyle.Regular, 1, 0); + case "1162": return new SccPositionAndStyle(Color.Green, FontStyle.Regular, 2, 0); + case "1242": return new SccPositionAndStyle(Color.Green, FontStyle.Regular, 3, 0); + case "1262": return new SccPositionAndStyle(Color.Green, FontStyle.Regular, 4, 0); + case "1542": return new SccPositionAndStyle(Color.Green, FontStyle.Regular, 5, 0); + case "1562": return new SccPositionAndStyle(Color.Green, FontStyle.Regular, 6, 0); + case "1642": return new SccPositionAndStyle(Color.Green, FontStyle.Regular, 7, 0); + case "1662": return new SccPositionAndStyle(Color.Green, FontStyle.Regular, 8, 0); + case "1742": return new SccPositionAndStyle(Color.Green, FontStyle.Regular, 9, 0); + case "1762": return new SccPositionAndStyle(Color.Green, FontStyle.Regular, 10, 0); + case "1042": return new SccPositionAndStyle(Color.Green, FontStyle.Regular, 11, 0); + case "1342": return new SccPositionAndStyle(Color.Green, FontStyle.Regular, 12, 0); + case "1362": return new SccPositionAndStyle(Color.Green, FontStyle.Regular, 13, 0); + case "1442": return new SccPositionAndStyle(Color.Green, FontStyle.Regular, 14, 0); + case "1462": return new SccPositionAndStyle(Color.Green, FontStyle.Regular, 15, 0); + + case "1143": return new SccPositionAndStyle(Color.Green, FontStyle.Underline, 1, 0); + case "1163": return new SccPositionAndStyle(Color.Green, FontStyle.Underline, 2, 0); + case "1243": return new SccPositionAndStyle(Color.Green, FontStyle.Underline, 3, 0); + case "1263": return new SccPositionAndStyle(Color.Green, FontStyle.Underline, 4, 0); + case "1543": return new SccPositionAndStyle(Color.Green, FontStyle.Underline, 5, 0); + case "1563": return new SccPositionAndStyle(Color.Green, FontStyle.Underline, 6, 0); + case "1643": return new SccPositionAndStyle(Color.Green, FontStyle.Underline, 7, 0); + case "1663": return new SccPositionAndStyle(Color.Green, FontStyle.Underline, 8, 0); + case "1743": return new SccPositionAndStyle(Color.Green, FontStyle.Underline, 9, 0); + case "1763": return new SccPositionAndStyle(Color.Green, FontStyle.Underline, 10, 0); + case "1043": return new SccPositionAndStyle(Color.Green, FontStyle.Underline, 11, 0); + case "1343": return new SccPositionAndStyle(Color.Green, FontStyle.Underline, 12, 0); + case "1363": return new SccPositionAndStyle(Color.Green, FontStyle.Underline, 13, 0); + case "1443": return new SccPositionAndStyle(Color.Green, FontStyle.Underline, 14, 0); + case "1463": return new SccPositionAndStyle(Color.Green, FontStyle.Underline, 15, 0); + + case "1144": return new SccPositionAndStyle(Color.Blue, FontStyle.Regular, 1, 0); + case "1164": return new SccPositionAndStyle(Color.Blue, FontStyle.Regular, 2, 0); + case "1244": return new SccPositionAndStyle(Color.Blue, FontStyle.Regular, 3, 0); + case "1264": return new SccPositionAndStyle(Color.Blue, FontStyle.Regular, 4, 0); + case "1544": return new SccPositionAndStyle(Color.Blue, FontStyle.Regular, 5, 0); + case "1564": return new SccPositionAndStyle(Color.Blue, FontStyle.Regular, 6, 0); + case "1644": return new SccPositionAndStyle(Color.Blue, FontStyle.Regular, 7, 0); + case "1664": return new SccPositionAndStyle(Color.Blue, FontStyle.Regular, 8, 0); + case "1744": return new SccPositionAndStyle(Color.Blue, FontStyle.Regular, 9, 0); + case "1764": return new SccPositionAndStyle(Color.Blue, FontStyle.Regular, 10, 0); + case "1044": return new SccPositionAndStyle(Color.Blue, FontStyle.Regular, 11, 0); + case "1344": return new SccPositionAndStyle(Color.Blue, FontStyle.Regular, 12, 0); + case "1364": return new SccPositionAndStyle(Color.Blue, FontStyle.Regular, 13, 0); + case "1444": return new SccPositionAndStyle(Color.Blue, FontStyle.Regular, 14, 0); + case "1464": return new SccPositionAndStyle(Color.Blue, FontStyle.Regular, 15, 0); + + case "1145": return new SccPositionAndStyle(Color.Blue, FontStyle.Underline, 1, 0); + case "1165": return new SccPositionAndStyle(Color.Blue, FontStyle.Underline, 2, 0); + case "1245": return new SccPositionAndStyle(Color.Blue, FontStyle.Underline, 3, 0); + case "1265": return new SccPositionAndStyle(Color.Blue, FontStyle.Underline, 4, 0); + case "1545": return new SccPositionAndStyle(Color.Blue, FontStyle.Underline, 5, 0); + case "1565": return new SccPositionAndStyle(Color.Blue, FontStyle.Underline, 6, 0); + case "1645": return new SccPositionAndStyle(Color.Blue, FontStyle.Underline, 7, 0); + case "1665": return new SccPositionAndStyle(Color.Blue, FontStyle.Underline, 8, 0); + case "1745": return new SccPositionAndStyle(Color.Blue, FontStyle.Underline, 9, 0); + case "1765": return new SccPositionAndStyle(Color.Blue, FontStyle.Underline, 10, 0); + case "1045": return new SccPositionAndStyle(Color.Blue, FontStyle.Underline, 11, 0); + case "1345": return new SccPositionAndStyle(Color.Blue, FontStyle.Underline, 12, 0); + case "1365": return new SccPositionAndStyle(Color.Blue, FontStyle.Underline, 13, 0); + case "1445": return new SccPositionAndStyle(Color.Blue, FontStyle.Underline, 14, 0); + case "1465": return new SccPositionAndStyle(Color.Blue, FontStyle.Underline, 15, 0); + + case "1146": return new SccPositionAndStyle(Color.Cyan, FontStyle.Regular, 1, 0); + case "1166": return new SccPositionAndStyle(Color.Cyan, FontStyle.Regular, 2, 0); + case "1246": return new SccPositionAndStyle(Color.Cyan, FontStyle.Regular, 3, 0); + case "1266": return new SccPositionAndStyle(Color.Cyan, FontStyle.Regular, 4, 0); + case "1546": return new SccPositionAndStyle(Color.Cyan, FontStyle.Regular, 5, 0); + case "1566": return new SccPositionAndStyle(Color.Cyan, FontStyle.Regular, 6, 0); + case "1646": return new SccPositionAndStyle(Color.Cyan, FontStyle.Regular, 7, 0); + case "1666": return new SccPositionAndStyle(Color.Cyan, FontStyle.Regular, 8, 0); + case "1746": return new SccPositionAndStyle(Color.Cyan, FontStyle.Regular, 9, 0); + case "1766": return new SccPositionAndStyle(Color.Cyan, FontStyle.Regular, 10, 0); + case "1046": return new SccPositionAndStyle(Color.Cyan, FontStyle.Regular, 11, 0); + case "1346": return new SccPositionAndStyle(Color.Cyan, FontStyle.Regular, 12, 0); + case "1366": return new SccPositionAndStyle(Color.Cyan, FontStyle.Regular, 13, 0); + case "1446": return new SccPositionAndStyle(Color.Cyan, FontStyle.Regular, 14, 0); + case "1466": return new SccPositionAndStyle(Color.Cyan, FontStyle.Regular, 15, 0); + + case "1147": return new SccPositionAndStyle(Color.Cyan, FontStyle.Underline, 1, 0); + case "1167": return new SccPositionAndStyle(Color.Cyan, FontStyle.Underline, 2, 0); + case "1247": return new SccPositionAndStyle(Color.Cyan, FontStyle.Underline, 3, 0); + case "1267": return new SccPositionAndStyle(Color.Cyan, FontStyle.Underline, 4, 0); + case "1547": return new SccPositionAndStyle(Color.Cyan, FontStyle.Underline, 5, 0); + case "1567": return new SccPositionAndStyle(Color.Cyan, FontStyle.Underline, 6, 0); + case "1647": return new SccPositionAndStyle(Color.Cyan, FontStyle.Underline, 7, 0); + case "1667": return new SccPositionAndStyle(Color.Cyan, FontStyle.Underline, 8, 0); + case "1747": return new SccPositionAndStyle(Color.Cyan, FontStyle.Underline, 9, 0); + case "1767": return new SccPositionAndStyle(Color.Cyan, FontStyle.Underline, 10, 0); + case "1047": return new SccPositionAndStyle(Color.Cyan, FontStyle.Underline, 11, 0); + case "1347": return new SccPositionAndStyle(Color.Cyan, FontStyle.Underline, 12, 0); + case "1367": return new SccPositionAndStyle(Color.Cyan, FontStyle.Underline, 13, 0); + case "1447": return new SccPositionAndStyle(Color.Cyan, FontStyle.Underline, 14, 0); + case "1467": return new SccPositionAndStyle(Color.Cyan, FontStyle.Underline, 15, 0); + + case "1148": return new SccPositionAndStyle(Color.Red, FontStyle.Regular, 1, 0); + case "1168": return new SccPositionAndStyle(Color.Red, FontStyle.Regular, 2, 0); + case "1248": return new SccPositionAndStyle(Color.Red, FontStyle.Regular, 3, 0); + case "1268": return new SccPositionAndStyle(Color.Red, FontStyle.Regular, 4, 0); + case "1548": return new SccPositionAndStyle(Color.Red, FontStyle.Regular, 5, 0); + case "1568": return new SccPositionAndStyle(Color.Red, FontStyle.Regular, 6, 0); + case "1648": return new SccPositionAndStyle(Color.Red, FontStyle.Regular, 7, 0); + case "1668": return new SccPositionAndStyle(Color.Red, FontStyle.Regular, 8, 0); + case "1748": return new SccPositionAndStyle(Color.Red, FontStyle.Regular, 9, 0); + case "1768": return new SccPositionAndStyle(Color.Red, FontStyle.Regular, 10, 0); + case "1048": return new SccPositionAndStyle(Color.Red, FontStyle.Regular, 11, 0); + case "1348": return new SccPositionAndStyle(Color.Red, FontStyle.Regular, 12, 0); + case "1368": return new SccPositionAndStyle(Color.Red, FontStyle.Regular, 13, 0); + case "1448": return new SccPositionAndStyle(Color.Red, FontStyle.Regular, 14, 0); + case "1468": return new SccPositionAndStyle(Color.Red, FontStyle.Regular, 15, 0); + + case "1149": return new SccPositionAndStyle(Color.Red, FontStyle.Underline, 1, 0); + case "1169": return new SccPositionAndStyle(Color.Red, FontStyle.Underline, 2, 0); + case "1249": return new SccPositionAndStyle(Color.Red, FontStyle.Underline, 3, 0); + case "1269": return new SccPositionAndStyle(Color.Red, FontStyle.Underline, 4, 0); + case "1549": return new SccPositionAndStyle(Color.Red, FontStyle.Underline, 5, 0); + case "1569": return new SccPositionAndStyle(Color.Red, FontStyle.Underline, 6, 0); + case "1649": return new SccPositionAndStyle(Color.Red, FontStyle.Underline, 7, 0); + case "1669": return new SccPositionAndStyle(Color.Red, FontStyle.Underline, 8, 0); + case "1749": return new SccPositionAndStyle(Color.Red, FontStyle.Underline, 9, 0); + case "1769": return new SccPositionAndStyle(Color.Red, FontStyle.Underline, 10, 0); + case "1049": return new SccPositionAndStyle(Color.Red, FontStyle.Underline, 11, 0); + case "1349": return new SccPositionAndStyle(Color.Red, FontStyle.Underline, 12, 0); + case "1369": return new SccPositionAndStyle(Color.Red, FontStyle.Underline, 13, 0); + case "1449": return new SccPositionAndStyle(Color.Red, FontStyle.Underline, 14, 0); + case "1469": return new SccPositionAndStyle(Color.Red, FontStyle.Underline, 15, 0); + + case "114a": return new SccPositionAndStyle(Color.Yellow, FontStyle.Regular, 1, 0); + case "116a": return new SccPositionAndStyle(Color.Yellow, FontStyle.Regular, 2, 0); + case "124a": return new SccPositionAndStyle(Color.Yellow, FontStyle.Regular, 3, 0); + case "126a": return new SccPositionAndStyle(Color.Yellow, FontStyle.Regular, 4, 0); + case "154a": return new SccPositionAndStyle(Color.Yellow, FontStyle.Regular, 5, 0); + case "156a": return new SccPositionAndStyle(Color.Yellow, FontStyle.Regular, 6, 0); + case "164a": return new SccPositionAndStyle(Color.Yellow, FontStyle.Regular, 7, 0); + case "166a": return new SccPositionAndStyle(Color.Yellow, FontStyle.Regular, 8, 0); + case "174a": return new SccPositionAndStyle(Color.Yellow, FontStyle.Regular, 9, 0); + case "176a": return new SccPositionAndStyle(Color.Yellow, FontStyle.Regular, 10, 0); + case "104a": return new SccPositionAndStyle(Color.Yellow, FontStyle.Regular, 11, 0); + case "134a": return new SccPositionAndStyle(Color.Yellow, FontStyle.Regular, 12, 0); + case "136a": return new SccPositionAndStyle(Color.Yellow, FontStyle.Regular, 13, 0); + case "144a": return new SccPositionAndStyle(Color.Yellow, FontStyle.Regular, 14, 0); + case "146a": return new SccPositionAndStyle(Color.Yellow, FontStyle.Regular, 15, 0); + + case "114b": return new SccPositionAndStyle(Color.Yellow, FontStyle.Underline, 1, 0); + case "116b": return new SccPositionAndStyle(Color.Yellow, FontStyle.Underline, 2, 0); + case "124b": return new SccPositionAndStyle(Color.Yellow, FontStyle.Underline, 3, 0); + case "126b": return new SccPositionAndStyle(Color.Yellow, FontStyle.Underline, 4, 0); + case "154b": return new SccPositionAndStyle(Color.Yellow, FontStyle.Underline, 5, 0); + case "156b": return new SccPositionAndStyle(Color.Yellow, FontStyle.Underline, 6, 0); + case "164b": return new SccPositionAndStyle(Color.Yellow, FontStyle.Underline, 7, 0); + case "166b": return new SccPositionAndStyle(Color.Yellow, FontStyle.Underline, 8, 0); + case "174b": return new SccPositionAndStyle(Color.Yellow, FontStyle.Underline, 9, 0); + case "176b": return new SccPositionAndStyle(Color.Yellow, FontStyle.Underline, 10, 0); + case "104b": return new SccPositionAndStyle(Color.Yellow, FontStyle.Underline, 11, 0); + case "134b": return new SccPositionAndStyle(Color.Yellow, FontStyle.Underline, 12, 0); + case "136b": return new SccPositionAndStyle(Color.Yellow, FontStyle.Underline, 13, 0); + case "144b": return new SccPositionAndStyle(Color.Yellow, FontStyle.Underline, 14, 0); + case "146b": return new SccPositionAndStyle(Color.Yellow, FontStyle.Underline, 15, 0); + + case "114c": return new SccPositionAndStyle(Color.Magenta, FontStyle.Regular, 1, 0); + case "116c": return new SccPositionAndStyle(Color.Magenta, FontStyle.Regular, 2, 0); + case "124c": return new SccPositionAndStyle(Color.Magenta, FontStyle.Regular, 3, 0); + case "126c": return new SccPositionAndStyle(Color.Magenta, FontStyle.Regular, 4, 0); + case "154c": return new SccPositionAndStyle(Color.Magenta, FontStyle.Regular, 5, 0); + case "156c": return new SccPositionAndStyle(Color.Magenta, FontStyle.Regular, 6, 0); + case "164c": return new SccPositionAndStyle(Color.Magenta, FontStyle.Regular, 7, 0); + case "166c": return new SccPositionAndStyle(Color.Magenta, FontStyle.Regular, 8, 0); + case "174c": return new SccPositionAndStyle(Color.Magenta, FontStyle.Regular, 9, 0); + case "176c": return new SccPositionAndStyle(Color.Magenta, FontStyle.Regular, 10, 0); + case "104c": return new SccPositionAndStyle(Color.Magenta, FontStyle.Regular, 11, 0); + case "134c": return new SccPositionAndStyle(Color.Magenta, FontStyle.Regular, 12, 0); + case "136c": return new SccPositionAndStyle(Color.Magenta, FontStyle.Regular, 13, 0); + case "144c": return new SccPositionAndStyle(Color.Magenta, FontStyle.Regular, 14, 0); + case "146c": return new SccPositionAndStyle(Color.Magenta, FontStyle.Regular, 15, 0); + + case "114d": return new SccPositionAndStyle(Color.Magenta, FontStyle.Underline, 1, 0); + case "116d": return new SccPositionAndStyle(Color.Magenta, FontStyle.Underline, 2, 0); + case "124d": return new SccPositionAndStyle(Color.Magenta, FontStyle.Underline, 3, 0); + case "126d": return new SccPositionAndStyle(Color.Magenta, FontStyle.Underline, 4, 0); + case "154d": return new SccPositionAndStyle(Color.Magenta, FontStyle.Underline, 5, 0); + case "156d": return new SccPositionAndStyle(Color.Magenta, FontStyle.Underline, 6, 0); + case "164d": return new SccPositionAndStyle(Color.Magenta, FontStyle.Underline, 7, 0); + case "166d": return new SccPositionAndStyle(Color.Magenta, FontStyle.Underline, 8, 0); + case "174d": return new SccPositionAndStyle(Color.Magenta, FontStyle.Underline, 9, 0); + case "176d": return new SccPositionAndStyle(Color.Magenta, FontStyle.Underline, 10, 0); + case "104d": return new SccPositionAndStyle(Color.Magenta, FontStyle.Underline, 11, 0); + case "134d": return new SccPositionAndStyle(Color.Magenta, FontStyle.Underline, 12, 0); + case "136d": return new SccPositionAndStyle(Color.Magenta, FontStyle.Underline, 13, 0); + case "144d": return new SccPositionAndStyle(Color.Magenta, FontStyle.Underline, 14, 0); + case "146d": return new SccPositionAndStyle(Color.Magenta, FontStyle.Underline, 15, 0); + + case "114e": return new SccPositionAndStyle(Color.White, FontStyle.Italic, 1, 0); + case "116e": return new SccPositionAndStyle(Color.White, FontStyle.Italic, 2, 0); + case "124e": return new SccPositionAndStyle(Color.White, FontStyle.Italic, 3, 0); + case "126e": return new SccPositionAndStyle(Color.White, FontStyle.Italic, 4, 0); + case "154e": return new SccPositionAndStyle(Color.White, FontStyle.Italic, 5, 0); + case "156e": return new SccPositionAndStyle(Color.White, FontStyle.Italic, 6, 0); + case "164e": return new SccPositionAndStyle(Color.White, FontStyle.Italic, 7, 0); + case "166e": return new SccPositionAndStyle(Color.White, FontStyle.Italic, 8, 0); + case "174e": return new SccPositionAndStyle(Color.White, FontStyle.Italic, 9, 0); + case "176e": return new SccPositionAndStyle(Color.White, FontStyle.Italic, 10, 0); + case "104e": return new SccPositionAndStyle(Color.White, FontStyle.Italic, 11, 0); + case "134e": return new SccPositionAndStyle(Color.White, FontStyle.Italic, 12, 0); + case "136e": return new SccPositionAndStyle(Color.White, FontStyle.Italic, 13, 0); + case "144e": return new SccPositionAndStyle(Color.White, FontStyle.Italic, 14, 0); + case "146e": return new SccPositionAndStyle(Color.White, FontStyle.Italic, 15, 0); + + case "114f": return new SccPositionAndStyle(Color.White, FontStyle.Underline | FontStyle.Italic, 1, 0); + case "116f": return new SccPositionAndStyle(Color.White, FontStyle.Underline | FontStyle.Italic, 2, 0); + case "124f": return new SccPositionAndStyle(Color.White, FontStyle.Underline | FontStyle.Italic, 3, 0); + case "126f": return new SccPositionAndStyle(Color.White, FontStyle.Underline | FontStyle.Italic, 4, 0); + case "154f": return new SccPositionAndStyle(Color.White, FontStyle.Underline | FontStyle.Italic, 5, 0); + case "156f": return new SccPositionAndStyle(Color.White, FontStyle.Underline | FontStyle.Italic, 6, 0); + case "164f": return new SccPositionAndStyle(Color.White, FontStyle.Underline | FontStyle.Italic, 7, 0); + case "166f": return new SccPositionAndStyle(Color.White, FontStyle.Underline | FontStyle.Italic, 8, 0); + case "174f": return new SccPositionAndStyle(Color.White, FontStyle.Underline | FontStyle.Italic, 9, 0); + case "176f": return new SccPositionAndStyle(Color.White, FontStyle.Underline | FontStyle.Italic, 10, 0); + case "104f": return new SccPositionAndStyle(Color.White, FontStyle.Underline | FontStyle.Italic, 11, 0); + case "134f": return new SccPositionAndStyle(Color.White, FontStyle.Underline | FontStyle.Italic, 12, 0); + case "136f": return new SccPositionAndStyle(Color.White, FontStyle.Underline | FontStyle.Italic, 13, 0); + case "144f": return new SccPositionAndStyle(Color.White, FontStyle.Underline | FontStyle.Italic, 14, 0); + case "146f": return new SccPositionAndStyle(Color.White, FontStyle.Underline | FontStyle.Italic, 15, 0); + + case "91d0": return new SccPositionAndStyle(Color.White, FontStyle.Regular, 1, 0); + case "9151": return new SccPositionAndStyle(Color.White, FontStyle.Underline, 1, 0); + case "91c2": return new SccPositionAndStyle(Color.Green, FontStyle.Regular, 1, 0); + case "9143": return new SccPositionAndStyle(Color.Green, FontStyle.Underline, 1, 0); + case "91c4": return new SccPositionAndStyle(Color.Blue, FontStyle.Regular, 1, 0); + case "9145": return new SccPositionAndStyle(Color.Blue, FontStyle.Underline, 1, 0); + case "9146": return new SccPositionAndStyle(Color.Cyan, FontStyle.Regular, 1, 0); + case "91c7": return new SccPositionAndStyle(Color.Cyan, FontStyle.Underline, 1, 0); + case "91c8": return new SccPositionAndStyle(Color.Red, FontStyle.Regular, 1, 0); + case "9149": return new SccPositionAndStyle(Color.Red, FontStyle.Underline, 1, 0); + case "914a": return new SccPositionAndStyle(Color.Yellow, FontStyle.Regular, 1, 0); + case "91cb": return new SccPositionAndStyle(Color.Yellow, FontStyle.Underline, 1, 0); + case "914c": return new SccPositionAndStyle(Color.Magenta, FontStyle.Regular, 1, 0); + case "91cd": return new SccPositionAndStyle(Color.Magenta, FontStyle.Underline, 1, 0); + + case "9170": return new SccPositionAndStyle(Color.White, FontStyle.Regular, 2, 0); + case "91f1": return new SccPositionAndStyle(Color.White, FontStyle.Underline, 2, 0); + case "9162": return new SccPositionAndStyle(Color.Green, FontStyle.Regular, 2, 0); + case "91e3": return new SccPositionAndStyle(Color.Green, FontStyle.Underline, 2, 0); + case "9164": return new SccPositionAndStyle(Color.Blue, FontStyle.Regular, 2, 0); + case "91e5": return new SccPositionAndStyle(Color.Blue, FontStyle.Underline, 2, 0); + case "91e6": return new SccPositionAndStyle(Color.Cyan, FontStyle.Regular, 2, 0); + case "9167": return new SccPositionAndStyle(Color.Cyan, FontStyle.Underline, 2, 0); + case "9168": return new SccPositionAndStyle(Color.Red, FontStyle.Regular, 2, 0); + case "91e9": return new SccPositionAndStyle(Color.Red, FontStyle.Underline, 2, 0); + case "91ea": return new SccPositionAndStyle(Color.Yellow, FontStyle.Regular, 2, 0); + case "916b": return new SccPositionAndStyle(Color.Yellow, FontStyle.Underline, 2, 0); + case "91ec": return new SccPositionAndStyle(Color.Magenta, FontStyle.Regular, 2, 0); + case "916d": return new SccPositionAndStyle(Color.Magenta, FontStyle.Underline, 2, 0); + + case "92d0": return new SccPositionAndStyle(Color.White, FontStyle.Regular, 3, 0); + case "9251": return new SccPositionAndStyle(Color.White, FontStyle.Underline, 3, 0); + case "92c2": return new SccPositionAndStyle(Color.Green, FontStyle.Regular, 3, 0); + case "9243": return new SccPositionAndStyle(Color.Green, FontStyle.Underline, 3, 0); + case "92c4": return new SccPositionAndStyle(Color.Blue, FontStyle.Regular, 3, 0); + case "9245": return new SccPositionAndStyle(Color.Blue, FontStyle.Underline, 3, 0); + case "9246": return new SccPositionAndStyle(Color.Cyan, FontStyle.Regular, 3, 0); + case "92c7": return new SccPositionAndStyle(Color.Cyan, FontStyle.Underline, 3, 0); + case "92c8": return new SccPositionAndStyle(Color.Red, FontStyle.Regular, 3, 0); + case "9249": return new SccPositionAndStyle(Color.Red, FontStyle.Underline, 3, 0); + case "924a": return new SccPositionAndStyle(Color.Yellow, FontStyle.Regular, 3, 0); + case "92cb": return new SccPositionAndStyle(Color.Yellow, FontStyle.Underline, 3, 0); + case "924c": return new SccPositionAndStyle(Color.Magenta, FontStyle.Regular, 3, 0); + case "92cd": return new SccPositionAndStyle(Color.Magenta, FontStyle.Underline, 3, 0); + + case "9270": return new SccPositionAndStyle(Color.White, FontStyle.Regular, 4, 0); + case "92f1": return new SccPositionAndStyle(Color.White, FontStyle.Underline, 4, 0); + case "9262": return new SccPositionAndStyle(Color.Green, FontStyle.Regular, 4, 0); + case "92e3": return new SccPositionAndStyle(Color.Green, FontStyle.Underline, 4, 0); + case "9264": return new SccPositionAndStyle(Color.Blue, FontStyle.Regular, 4, 0); + case "92e5": return new SccPositionAndStyle(Color.Blue, FontStyle.Underline, 4, 0); + case "92e6": return new SccPositionAndStyle(Color.Cyan, FontStyle.Regular, 4, 0); + case "9267": return new SccPositionAndStyle(Color.Cyan, FontStyle.Underline, 4, 0); + case "9268": return new SccPositionAndStyle(Color.Red, FontStyle.Regular, 4, 0); + case "92e9": return new SccPositionAndStyle(Color.Red, FontStyle.Underline, 4, 0); + case "92ea": return new SccPositionAndStyle(Color.Yellow, FontStyle.Regular, 4, 0); + case "926b": return new SccPositionAndStyle(Color.Yellow, FontStyle.Underline, 4, 0); + case "92ec": return new SccPositionAndStyle(Color.Magenta, FontStyle.Regular, 4, 0); + case "926d": return new SccPositionAndStyle(Color.Magenta, FontStyle.Underline, 4, 0); + + case "15d0": return new SccPositionAndStyle(Color.White, FontStyle.Regular, 5, 0); + case "1551": return new SccPositionAndStyle(Color.White, FontStyle.Underline, 5, 0); + case "15c2": return new SccPositionAndStyle(Color.Green, FontStyle.Regular, 5, 0); + // case "1543": return new SCCPositionAndStyle(Color.Green, FontStyle.Underline, 5, 0); + case "15c4": return new SccPositionAndStyle(Color.Blue, FontStyle.Regular, 5, 0); + // case "1545": return new SCCPositionAndStyle(Color.Blue, FontStyle.Underline, 5, 0); + // case "1546": return new SCCPositionAndStyle(Color.Cyan, FontStyle.Regular, 5, 0); + case "15c7": return new SccPositionAndStyle(Color.Cyan, FontStyle.Underline, 5, 0); + case "15c8": return new SccPositionAndStyle(Color.Red, FontStyle.Regular, 5, 0); + //case "1549": return new SCCPositionAndStyle(Color.Red, FontStyle.Underline, 5, 0); + //case "154a": return new SCCPositionAndStyle(Color.Yellow, FontStyle.Regular, 5, 0); + case "15cb": return new SccPositionAndStyle(Color.Yellow, FontStyle.Underline, 5, 0); + //case "154c": return new SCCPositionAndStyle(Color.Magenta, FontStyle.Regular, 5, 0); + case "15cd": return new SccPositionAndStyle(Color.Magenta, FontStyle.Underline, 5, 0); + + case "1570": return new SccPositionAndStyle(Color.White, FontStyle.Regular, 6, 0); + case "15f1": return new SccPositionAndStyle(Color.White, FontStyle.Underline, 6, 0); + //case "1562": return new SCCPositionAndStyle(Color.Green, FontStyle.Regular, 6, 0); + //case "15e3": return new SCCPositionAndStyle(Color.Green, FontStyle.Underline, 6, 0); + //case "1564": return new SCCPositionAndStyle(Color.Blue, FontStyle.Regular, 6, 0); + case "15e5": return new SccPositionAndStyle(Color.Blue, FontStyle.Underline, 6, 0); + case "15e6": return new SccPositionAndStyle(Color.Cyan, FontStyle.Regular, 6, 0); + //case "1567": return new SCCPositionAndStyle(Color.Cyan, FontStyle.Underline, 6, 0); + //case "1568": return new SCCPositionAndStyle(Color.Red, FontStyle.Regular, 6, 0); + case "15e9": return new SccPositionAndStyle(Color.Red, FontStyle.Underline, 6, 0); + case "15ea": return new SccPositionAndStyle(Color.Yellow, FontStyle.Regular, 6, 0); + //case "156b": return new SCCPositionAndStyle(Color.Yellow, FontStyle.Underline, 6, 0); + case "15ec": return new SccPositionAndStyle(Color.Magenta, FontStyle.Regular, 6, 0); + //case "156d": return new SCCPositionAndStyle(Color.Magenta, FontStyle.Underline, 6, 0); + + case "16d0": return new SccPositionAndStyle(Color.White, FontStyle.Regular, 7, 0); + case "1651": return new SccPositionAndStyle(Color.White, FontStyle.Underline, 7, 0); + case "16c2": return new SccPositionAndStyle(Color.Green, FontStyle.Regular, 7, 0); + //case "1643": return new SCCPositionAndStyle(Color.Green, FontStyle.Underline, 7, 0); + case "16c4": return new SccPositionAndStyle(Color.Blue, FontStyle.Regular, 7, 0); + //case "1645": return new SCCPositionAndStyle(Color.Blue, FontStyle.Underline, 7, 0); + //case "1646": return new SCCPositionAndStyle(Color.Cyan, FontStyle.Regular, 7, 0); + case "16c7": return new SccPositionAndStyle(Color.Cyan, FontStyle.Underline, 7, 0); + case "16c8": return new SccPositionAndStyle(Color.Red, FontStyle.Regular, 7, 0); + //case "1649": return new SCCPositionAndStyle(Color.Red, FontStyle.Underline, 7, 0); + //case "164a": return new SCCPositionAndStyle(Color.Yellow, FontStyle.Regular, 7, 0); + case "16cb": return new SccPositionAndStyle(Color.Yellow, FontStyle.Underline, 7, 0); + //case "164c": return new SCCPositionAndStyle(Color.Magenta, FontStyle.Regular, 7, 0); + case "16cd": return new SccPositionAndStyle(Color.Magenta, FontStyle.Underline, 7, 0); + + case "1670": return new SccPositionAndStyle(Color.White, FontStyle.Regular, 8, 0); + case "16f1": return new SccPositionAndStyle(Color.White, FontStyle.Underline, 8, 0); + //case "1662": return new SCCPositionAndStyle(Color.Green, FontStyle.Regular, 8, 0); + case "16e3": return new SccPositionAndStyle(Color.Green, FontStyle.Underline, 8, 0); + //case "1664": return new SCCPositionAndStyle(Color.Blue, FontStyle.Regular, 8, 0); + case "16e5": return new SccPositionAndStyle(Color.Blue, FontStyle.Underline, 8, 0); + case "16e6": return new SccPositionAndStyle(Color.Cyan, FontStyle.Regular, 8, 0); + //case "1667": return new SCCPositionAndStyle(Color.Cyan, FontStyle.Underline, 8, 0); + //case "1668": return new SCCPositionAndStyle(Color.Red, FontStyle.Regular, 8, 0); + case "16e9": return new SccPositionAndStyle(Color.Red, FontStyle.Underline, 8, 0); + case "16ea": return new SccPositionAndStyle(Color.Yellow, FontStyle.Regular, 8, 0); + //case "166b": return new SCCPositionAndStyle(Color.Yellow, FontStyle.Underline, 8, 0); + case "16ec": return new SccPositionAndStyle(Color.Magenta, FontStyle.Regular, 8, 0); + //case "166d": return new SCCPositionAndStyle(Color.Magenta, FontStyle.Underline, 8, 0); + + case "97d0": return new SccPositionAndStyle(Color.White, FontStyle.Regular, 9, 0); + case "9751": return new SccPositionAndStyle(Color.White, FontStyle.Underline, 9, 0); + case "97c2": return new SccPositionAndStyle(Color.Green, FontStyle.Regular, 9, 0); + case "9743": return new SccPositionAndStyle(Color.Green, FontStyle.Underline, 9, 0); + case "97c4": return new SccPositionAndStyle(Color.Blue, FontStyle.Regular, 9, 0); + case "9745": return new SccPositionAndStyle(Color.Blue, FontStyle.Underline, 9, 0); + case "9746": return new SccPositionAndStyle(Color.Cyan, FontStyle.Regular, 9, 0); + case "97c7": return new SccPositionAndStyle(Color.Cyan, FontStyle.Underline, 9, 0); + case "97c8": return new SccPositionAndStyle(Color.Red, FontStyle.Regular, 9, 0); + case "9749": return new SccPositionAndStyle(Color.Red, FontStyle.Underline, 9, 0); + case "974a": return new SccPositionAndStyle(Color.Yellow, FontStyle.Regular, 9, 0); + case "97cb": return new SccPositionAndStyle(Color.Yellow, FontStyle.Underline, 9, 0); + case "974c": return new SccPositionAndStyle(Color.Magenta, FontStyle.Regular, 9, 0); + case "97cd": return new SccPositionAndStyle(Color.Magenta, FontStyle.Underline, 9, 0); + + case "9770": return new SccPositionAndStyle(Color.White, FontStyle.Regular, 10, 0); + case "97f1": return new SccPositionAndStyle(Color.White, FontStyle.Underline, 10, 0); + case "9762": return new SccPositionAndStyle(Color.Green, FontStyle.Regular, 10, 0); + case "97e3": return new SccPositionAndStyle(Color.Green, FontStyle.Underline, 10, 0); + case "9764": return new SccPositionAndStyle(Color.Blue, FontStyle.Regular, 10, 0); + case "97e5": return new SccPositionAndStyle(Color.Blue, FontStyle.Underline, 10, 0); + case "97e6": return new SccPositionAndStyle(Color.Cyan, FontStyle.Regular, 10, 0); + case "9767": return new SccPositionAndStyle(Color.Cyan, FontStyle.Underline, 10, 0); + case "9768": return new SccPositionAndStyle(Color.Red, FontStyle.Regular, 10, 0); + case "97e9": return new SccPositionAndStyle(Color.Red, FontStyle.Underline, 10, 0); + case "97ea": return new SccPositionAndStyle(Color.Yellow, FontStyle.Regular, 10, 0); + case "976b": return new SccPositionAndStyle(Color.Yellow, FontStyle.Underline, 10, 0); + case "97ec": return new SccPositionAndStyle(Color.Magenta, FontStyle.Regular, 10, 0); + case "976d": return new SccPositionAndStyle(Color.Magenta, FontStyle.Underline, 10, 0); + + case "10d0": return new SccPositionAndStyle(Color.White, FontStyle.Regular, 11, 0); + case "1051": return new SccPositionAndStyle(Color.White, FontStyle.Underline, 11, 0); + case "10c2": return new SccPositionAndStyle(Color.Green, FontStyle.Regular, 11, 0); + //case "1043": return new SCCPositionAndStyle(Color.Green, FontStyle.Underline, 11, 0); + case "10c4": return new SccPositionAndStyle(Color.Blue, FontStyle.Regular, 11, 0); + //case "1045": return new SCCPositionAndStyle(Color.Blue, FontStyle.Underline, 11, 0); + //case "1046": return new SCCPositionAndStyle(Color.Cyan, FontStyle.Regular, 11, 0); + case "10c7": return new SccPositionAndStyle(Color.Cyan, FontStyle.Underline, 11, 0); + case "10c8": return new SccPositionAndStyle(Color.Red, FontStyle.Regular, 11, 0); + //case "1049": return new SCCPositionAndStyle(Color.Red, FontStyle.Underline, 11, 0); + //case "104a": return new SCCPositionAndStyle(Color.Yellow, FontStyle.Regular, 11, 0); + case "10cb": return new SccPositionAndStyle(Color.Yellow, FontStyle.Underline, 11, 0); + //case "104c": return new SCCPositionAndStyle(Color.Magenta, FontStyle.Regular, 11, 0); + case "10cd": return new SccPositionAndStyle(Color.Magenta, FontStyle.Underline, 11, 0); + + case "13d0": return new SccPositionAndStyle(Color.White, FontStyle.Regular, 12, 0); + case "1351": return new SccPositionAndStyle(Color.White, FontStyle.Underline, 12, 0); + case "13c2": return new SccPositionAndStyle(Color.Green, FontStyle.Regular, 12, 0); + //case "1343": return new SCCPositionAndStyle(Color.Green, FontStyle.Underline, 12, 0); + case "13c4": return new SccPositionAndStyle(Color.Blue, FontStyle.Regular, 12, 0); + //case "1345": return new SCCPositionAndStyle(Color.Blue, FontStyle.Underline, 12, 0); + //case "1346": return new SCCPositionAndStyle(Color.Cyan, FontStyle.Regular, 12, 0); + case "13c7": return new SccPositionAndStyle(Color.Cyan, FontStyle.Underline, 12, 0); + case "13c8": return new SccPositionAndStyle(Color.Red, FontStyle.Regular, 12, 0); + //case "1349": return new SCCPositionAndStyle(Color.Red, FontStyle.Underline, 12, 0); + //case "134a": return new SCCPositionAndStyle(Color.Yellow, FontStyle.Regular, 12, 0); + case "13cb": return new SccPositionAndStyle(Color.Yellow, FontStyle.Underline, 12, 0); + //case "134c": return new SCCPositionAndStyle(Color.Magenta, FontStyle.Regular, 12, 0); + case "13cd": return new SccPositionAndStyle(Color.Magenta, FontStyle.Underline, 12, 0); + + case "1370": return new SccPositionAndStyle(Color.White, FontStyle.Regular, 13, 0); + case "13f1": return new SccPositionAndStyle(Color.White, FontStyle.Underline, 13, 0); + //case "1362": return new SCCPositionAndStyle(Color.Green, FontStyle.Regular, 13, 0); + case "13e3": return new SccPositionAndStyle(Color.Green, FontStyle.Underline, 13, 0); + //case "1364": return new SCCPositionAndStyle(Color.Blue, FontStyle.Regular, 13, 0); + case "13e5": return new SccPositionAndStyle(Color.Blue, FontStyle.Underline, 13, 0); + case "13e6": return new SccPositionAndStyle(Color.Cyan, FontStyle.Regular, 13, 0); + //case "1367": return new SCCPositionAndStyle(Color.Cyan, FontStyle.Underline, 13, 0); + //case "1368": return new SCCPositionAndStyle(Color.Red, FontStyle.Regular, 13, 0); + case "13e9": return new SccPositionAndStyle(Color.Red, FontStyle.Underline, 13, 0); + case "13ea": return new SccPositionAndStyle(Color.Yellow, FontStyle.Regular, 13, 0); + //case "136b": return new SCCPositionAndStyle(Color.Yellow, FontStyle.Underline, 13, 0); + case "13ec": return new SccPositionAndStyle(Color.Magenta, FontStyle.Regular, 13, 0); + //case "136d": return new SCCPositionAndStyle(Color.Magenta, FontStyle.Underline, 13, 0); + + case "94d0": return new SccPositionAndStyle(Color.White, FontStyle.Regular, 14, 0); + case "9451": return new SccPositionAndStyle(Color.White, FontStyle.Underline, 14, 0); + case "94c2": return new SccPositionAndStyle(Color.Green, FontStyle.Regular, 14, 0); + case "9443": return new SccPositionAndStyle(Color.Green, FontStyle.Underline, 14, 0); + case "94c4": return new SccPositionAndStyle(Color.Blue, FontStyle.Regular, 14, 0); + case "9445": return new SccPositionAndStyle(Color.Blue, FontStyle.Underline, 14, 0); + case "9446": return new SccPositionAndStyle(Color.Cyan, FontStyle.Regular, 14, 0); + case "94c7": return new SccPositionAndStyle(Color.Cyan, FontStyle.Underline, 14, 0); + case "94c8": return new SccPositionAndStyle(Color.Red, FontStyle.Regular, 14, 0); + case "9449": return new SccPositionAndStyle(Color.Red, FontStyle.Underline, 14, 0); + case "944a": return new SccPositionAndStyle(Color.Yellow, FontStyle.Regular, 14, 0); + case "94cb": return new SccPositionAndStyle(Color.Yellow, FontStyle.Underline, 14, 0); + case "944c": return new SccPositionAndStyle(Color.Magenta, FontStyle.Regular, 14, 0); + case "94cd": return new SccPositionAndStyle(Color.Magenta, FontStyle.Underline, 14, 0); + + case "9470": return new SccPositionAndStyle(Color.White, FontStyle.Regular, 15, 0); + case "94f1": return new SccPositionAndStyle(Color.White, FontStyle.Underline, 15, 0); + case "9462": return new SccPositionAndStyle(Color.Green, FontStyle.Regular, 15, 0); + case "94e3": return new SccPositionAndStyle(Color.Green, FontStyle.Underline, 15, 0); + case "9464": return new SccPositionAndStyle(Color.Blue, FontStyle.Regular, 15, 0); + case "94e5": return new SccPositionAndStyle(Color.Blue, FontStyle.Underline, 15, 0); + case "94e6": return new SccPositionAndStyle(Color.Cyan, FontStyle.Regular, 15, 0); + case "9467": return new SccPositionAndStyle(Color.Cyan, FontStyle.Underline, 15, 0); + case "9468": return new SccPositionAndStyle(Color.Red, FontStyle.Regular, 15, 0); + case "94e9": return new SccPositionAndStyle(Color.Red, FontStyle.Underline, 15, 0); + case "94ea": return new SccPositionAndStyle(Color.Yellow, FontStyle.Regular, 15, 0); + case "946b": return new SccPositionAndStyle(Color.Yellow, FontStyle.Underline, 15, 0); + case "94ec": return new SccPositionAndStyle(Color.Magenta, FontStyle.Regular, 15, 0); + case "946d": return new SccPositionAndStyle(Color.Magenta, FontStyle.Underline, 15, 0); + + //Columns 4-28 + + case "9152": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 1, 4); + case "91d3": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 1, 4); + case "9154": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 1, 8); + case "91d5": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 1, 8); + case "91d6": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 1, 12); + case "9157": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 1, 12); + case "9158": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 1, 16); + case "91d9": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 1, 16); + case "91da": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 1, 20); + case "915b": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 1, 20); + case "91dc": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 1, 24); + case "915d": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 1, 24); + case "915e": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 1, 28); + case "91df": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 1, 28); + + case "91f2": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 2, 4); + case "9173": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 2, 4); + case "91f4": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 2, 8); + case "9175": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 2, 8); + case "9176": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 2, 12); + case "91f7": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 2, 12); + case "91f8": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 2, 16); + case "9179": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 2, 16); + case "917a": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 2, 20); + case "91fb": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 2, 20); + case "917c": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 2, 24); + case "91fd": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 2, 24); + case "91fe": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 2, 28); + case "917f": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 2, 28); + + case "9252": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 3, 4); + case "92d3": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 3, 4); + case "9254": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 3, 8); + case "92d5": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 3, 8); + case "92d6": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 3, 12); + case "9257": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 3, 12); + case "9258": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 3, 16); + case "92d9": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 3, 16); + case "92da": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 3, 20); + case "925b": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 3, 20); + case "92dc": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 3, 24); + case "925d": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 3, 24); + case "925e": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 3, 28); + case "92df": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 3, 28); + + case "92f2": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 4, 4); + case "9273": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 4, 4); + case "92f4": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 4, 8); + case "9275": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 4, 8); + case "9276": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 4, 12); + case "92f7": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 4, 12); + case "92f8": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 4, 16); + case "9279": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 4, 16); + case "927a": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 4, 20); + case "92fb": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 4, 20); + case "927c": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 4, 24); + case "92fd": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 4, 24); + case "92fe": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 4, 28); + case "927f": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 4, 28); + + case "1552": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 5, 4); + case "15d3": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 5, 4); + case "1554": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 5, 8); + case "15d5": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 5, 8); + case "15d6": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 5, 12); + case "1557": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 5, 12); + case "1558": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 5, 16); + case "15d9": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 5, 16); + case "15da": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 5, 20); + case "155b": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 5, 20); + case "15dc": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 5, 24); + case "155d": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 5, 24); + case "155e": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 5, 28); + case "15df": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 5, 28); + + case "15f2": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 6, 4); + case "1573": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 6, 4); + case "15f4": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 6, 8); + case "1575": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 6, 8); + case "1576": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 6, 12); + case "15f7": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 6, 12); + case "15f8": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 6, 16); + case "1579": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 6, 16); + case "157a": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 6, 20); + case "15fb": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 6, 20); + case "157c": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 6, 24); + case "15fd": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 6, 24); + case "15fe": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 6, 28); + case "157f": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 6, 28); + + case "1652": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 7, 4); + case "16d3": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 7, 4); + case "1654": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 7, 8); + case "16d5": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 7, 8); + case "16d6": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 7, 12); + case "1657": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 7, 12); + case "1658": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 7, 16); + case "16d9": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 7, 16); + case "16da": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 7, 20); + case "165b": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 7, 20); + case "16dc": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 7, 24); + case "165d": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 7, 24); + case "165e": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 7, 28); + case "16df": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 7, 28); + + case "16f2": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 8, 4); + case "1673": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 8, 4); + case "16f4": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 8, 8); + case "1675": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 8, 8); + case "1676": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 8, 12); + case "16f7": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 8, 12); + case "16f8": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 8, 16); + case "1679": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 8, 16); + case "167a": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 8, 20); + case "16fb": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 8, 20); + case "167c": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 8, 24); + case "16fd": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 8, 24); + case "16fe": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 8, 28); + case "167f": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 8, 28); + + case "9752": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 9, 4); + case "97d3": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 9, 4); + case "9754": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 9, 8); + case "97d5": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 9, 8); + case "97d6": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 9, 12); + case "9757": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 9, 12); + case "9758": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 9, 16); + case "97d9": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 9, 16); + case "97da": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 9, 20); + case "975b": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 9, 20); + case "97dc": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 9, 24); + case "975d": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 9, 24); + case "975e": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 9, 28); + case "97df": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 9, 28); + + case "97f2": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 10, 4); + case "9773": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 10, 4); + case "97f4": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 10, 8); + case "9775": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 10, 8); + case "9776": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 10, 12); + case "97f7": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 10, 12); + case "97f8": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 10, 16); + case "9779": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 10, 16); + case "977a": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 10, 20); + case "97fb": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 10, 20); + case "977c": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 10, 24); + case "97fd": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 10, 24); + case "97fe": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 10, 28); + case "977f": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 10, 28); + + case "1052": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 11, 4); + case "10d3": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 11, 4); + case "1054": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 11, 8); + case "10d5": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 11, 8); + case "10d6": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 11, 12); + case "1057": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 11, 12); + case "1058": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 11, 16); + case "10d9": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 11, 16); + case "10da": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 11, 20); + case "105b": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 11, 20); + case "10dc": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 11, 24); + case "105d": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 11, 24); + case "105e": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 11, 28); + case "10df": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 11, 28); + + case "1352": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 12, 4); + case "13d3": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 12, 4); + case "1354": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 12, 8); + case "13d5": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 12, 8); + case "13d6": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 12, 12); + case "1357": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 12, 12); + case "1358": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 12, 16); + case "13d9": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 12, 16); + case "13da": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 12, 20); + case "135b": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 12, 20); + case "13dc": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 12, 24); + case "135d": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 12, 24); + case "135e": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 12, 28); + case "13df": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 12, 28); + + case "13f2": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 13, 4); + case "1373": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 13, 4); + case "13f4": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 13, 8); + case "1375": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 13, 8); + case "1376": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 13, 12); + case "13f7": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 13, 12); + case "13f8": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 13, 16); + case "1379": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 13, 16); + case "137a": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 13, 20); + case "13fb": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 13, 20); + case "137c": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 13, 24); + case "13fd": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 13, 24); + case "13fe": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 13, 28); + case "137f": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 13, 28); + + case "9452": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 14, 4); + case "94d3": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 14, 4); + case "9454": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 14, 8); + case "94d5": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 14, 8); + case "94d6": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 14, 12); + case "9457": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 14, 12); + case "9458": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 14, 16); + case "94d9": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 14, 16); + case "94da": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 14, 20); + case "945b": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 14, 20); + case "94dc": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 14, 24); + case "945d": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 14, 24); + case "945e": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 14, 28); + case "94df": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 14, 28); + + case "94f2": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 15, 4); + case "9473": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 15, 4); + case "94f4": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 15, 8); + case "9475": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 15, 8); + case "9476": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 15, 12); + case "94f7": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 15, 12); + case "94f8": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 15, 16); + case "9479": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 15, 16); + case "947a": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 15, 20); + case "94fb": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 15, 20); + case "947c": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 15, 24); + case "94fd": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 15, 24); + case "94fe": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, 15, 28); + case "947f": return new SccPositionAndStyle(Color.Transparent, FontStyle.Underline, 15, 28); + + // mid-row commands + case "9120": return new SccPositionAndStyle(Color.White, FontStyle.Regular, -1, -1); + case "91a1": return new SccPositionAndStyle(Color.White, FontStyle.Underline, -1, -1); + case "91a2": return new SccPositionAndStyle(Color.Green, FontStyle.Regular, -1, -1); + case "9123": return new SccPositionAndStyle(Color.Green, FontStyle.Underline, -1, -1); + case "91a4": return new SccPositionAndStyle(Color.Blue, FontStyle.Regular, -1, -1); + case "9125": return new SccPositionAndStyle(Color.Blue, FontStyle.Underline, -1, -1); + case "9126": return new SccPositionAndStyle(Color.Cyan, FontStyle.Regular, -1, -1); + case "91a7": return new SccPositionAndStyle(Color.Cyan, FontStyle.Underline, -1, -1); + case "91a8": return new SccPositionAndStyle(Color.Red, FontStyle.Regular, -1, -1); + case "9129": return new SccPositionAndStyle(Color.Red, FontStyle.Underline, -1, -1); + case "912a": return new SccPositionAndStyle(Color.Yellow, FontStyle.Regular, -1, -1); + case "91ab": return new SccPositionAndStyle(Color.Yellow, FontStyle.Underline, -1, -1); + case "912c": return new SccPositionAndStyle(Color.Magenta, FontStyle.Regular, -1, -1); + case "91ad": return new SccPositionAndStyle(Color.Magenta, FontStyle.Underline, -1, -1); + + case "91ae": return new SccPositionAndStyle(Color.Transparent, FontStyle.Italic, -1, -1); + case "912f": return new SccPositionAndStyle(Color.Transparent, FontStyle.Italic | FontStyle.Underline, -1, -1); + + case "94a8": return new SccPositionAndStyle(Color.Transparent, FontStyle.Regular, -1, -1); // turn flash on + } + return null; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + Paragraph p = null; + foreach (string line in lines) + { + string s = line.Trim(); + var match = RegexTimeCodes.Match(s); + if (match.Success) + { + TimeCode startTime = ParseTimeCode(s.Substring(0, match.Length - 1)); + string text = GetSccText(s.Substring(match.Index), ref _errorCount); + + if (text == "942c 942c" || text == "942c") + { + p.EndTime = new TimeCode(startTime.TotalMilliseconds); + } + else + { + p = new Paragraph(startTime, new TimeCode(startTime.TotalMilliseconds), text); + subtitle.Paragraphs.Add(p); + } + } + } + for (int i = subtitle.Paragraphs.Count - 2; i >= 0; i--) + { + p = subtitle.GetParagraphOrDefault(i); + Paragraph next = subtitle.GetParagraphOrDefault(i + 1); + if (p != null && next != null && p.EndTime.TotalMilliseconds == p.StartTime.TotalMilliseconds) + p.EndTime = new TimeCode(next.StartTime.TotalMilliseconds); + if (next != null && string.IsNullOrEmpty(next.Text)) + subtitle.Paragraphs.Remove(next); + } + p = subtitle.GetParagraphOrDefault(0); + if (p != null && string.IsNullOrEmpty(p.Text)) + subtitle.Paragraphs.Remove(p); + + subtitle.Renumber(); + } + + public static string GetSccText(string s, ref int errorCount) + { + int y = 0; + string[] parts = s.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); + var sb = new StringBuilder(); + bool first = true; + bool italicOn = false; + int k = 0; + while (k < parts.Length) + { + string part = parts[k]; + if (part.Length == 4) + { + if (part != "94ae" && part != "9420" && part != "94ad" && part != "9426") + { + // Spanish inverted question mark (extended char) + if (part == "91b3" && k < parts.Length - 1 && parts[k + 1] == "91b3") + { + sb.Append("¿"); + k+=2; + continue; + } + + // Spanish inverted exclamation mark (extended char) + if (part == "a180" && k < parts.Length - 2 && parts[k + 1] == "92a7" && parts[k + 2] == "92a7") + { + sb.Append("¡"); + k += 3; + continue; + } + + if (part[0] == '9' || part[0] == '8') + { + if (k + 1 < parts.Length && parts[k + 1] == part) + k++; + } + + var cp = GetColorAndPosition(part); + if (cp != null) + { + if (cp.Y > 0 && y >= 0 && cp.Y > y && !sb.ToString().EndsWith(Environment.NewLine) && !string.IsNullOrWhiteSpace(sb.ToString())) + sb.AppendLine(); + if (cp.Y > 0) + y = cp.Y; + if ((cp.Style & FontStyle.Italic) == FontStyle.Italic && !italicOn) + { + sb.Append(""); + italicOn = true; + } + else if (cp.Style == FontStyle.Regular && italicOn) + { + sb.Append(""); + italicOn = false; + } + } + else + { + switch (part) + { + case "9440": + case "94e0": + if (!sb.ToString().EndsWith(Environment.NewLine)) + sb.AppendLine(); + break; + case "2c75": + case "2cf2": + case "2c6f": + case "2c6e": + case "2c6d": + case "2c6c": + case "2c6b": + case "2c6a": + case "2c69": + case "2c68": + case "2c67": + case "2c66": + case "2c65": + case "2c64": + case "2c63": + case "2c62": + case "2c61": + sb.Append(GetLetter(part.Substring(2, 2))); + break; + case "2c52": + case "2c94": + break; + default: + var result = GetLetter(part); + if (result == null) + { + sb.Append(GetLetter(part.Substring(0, 2))); + sb.Append(GetLetter(part.Substring(2, 2))); + } + else + { + sb.Append(result); + } + break; + } + } + } + } + else if (part.Length > 0) + { + if (!first) + errorCount++; + } + first = false; + k++; + } + string res = sb.ToString().Replace("", string.Empty).Replace("", string.Empty); + //res = res.Replace("♪♪", "♪"); + res = res.Replace(" ", " ").Replace(" ", " ").Replace(Environment.NewLine + " ", Environment.NewLine).Trim(); + if (res.Contains("") && !res.Contains("")) + res += ""; + //res = res.Replace("aã", "ã"); + //res = res.Replace("oõ", "õ"); + return HtmlUtil.FixInvalidItalicTags(res); + } + + private static string GetLetter(string hexCode) + { + int index = LetterCodes.IndexOf(hexCode.ToLower(CultureInfo.InvariantCulture)); + if (index < 0) + return null; + + return Letters[index]; + } + + private static TimeCode ParseTimeCode(string start) + { + string[] arr = start.Split(new[] { ':', ';', ',' }, StringSplitOptions.RemoveEmptyEntries); + + int milliseconds = (int)((1000 / Configuration.Settings.General.CurrentFrameRate) * int.Parse(arr[3])); + if (milliseconds > 999) + milliseconds = 999; + + return new TimeCode(int.Parse(arr[0]), int.Parse(arr[1]), int.Parse(arr[2]), milliseconds); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/ScenaristClosedCaptionsDropFrame.cs b/libse/SubtitleFormats/ScenaristClosedCaptionsDropFrame.cs new file mode 100644 index 000000000..bcc8a5c55 --- /dev/null +++ b/libse/SubtitleFormats/ScenaristClosedCaptionsDropFrame.cs @@ -0,0 +1,23 @@ +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + + public class ScenaristClosedCaptionsDropFrame : ScenaristClosedCaptions + { + //00:01:00:29 9420 9420 94ae 94ae 94d0 94d0 4920 f761 7320 ... semi colon (instead of colon) before frame number is used to indicate drop frame + private const string _timeCodeRegEx = @"^\d+:\d\d:\d\d[;,]\d\d\t"; + private static readonly Regex _regex = new Regex(_timeCodeRegEx, RegexOptions.Compiled); + protected override Regex RegexTimeCodes { get { return _regex; } } + + public ScenaristClosedCaptionsDropFrame() + { + DropFrame = true; + } + + public override string Name + { + get { return "Scenarist Closed Captions Drop Frame"; } + } + } +} diff --git a/libse/SubtitleFormats/SmilTimesheetData.cs b/libse/SubtitleFormats/SmilTimesheetData.cs new file mode 100644 index 000000000..2cc03a24a --- /dev/null +++ b/libse/SubtitleFormats/SmilTimesheetData.cs @@ -0,0 +1,217 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + /// + /// http://wam.inrialpes.fr/timesheets/annotations/video.html + /// + public class SmilTimesheetData : SubtitleFormat + { + public override string Extension + { + get { return ".html"; } + } + + public override string Name + { + get { return "SMIL Timesheet"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var sb = new StringBuilder(); + foreach (string l in lines) + sb.AppendLine(l); + if (!sb.ToString().Contains(" data-begin")) + return false; + + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + string header = +@" + +" + Environment.NewLine + +"\t" + Environment.NewLine + +"\tSMIL Timesheet" + @" + +" + Environment.NewLine + +"\t
" + Environment.NewLine + +"\t\t" + Environment.NewLine; + + const string paragraphWriteFormatOpen = "\t\t

\r\n{1}\r\n

"; + const string paragraphWriteFormat = "\t\t

\r\n{2}\r\n

"; + int count = 1; + var sb = new StringBuilder(); + sb.AppendLine(header.Replace("_TITLE_", title)); + foreach (Paragraph p in subtitle.Paragraphs) + { + Paragraph next = subtitle.GetParagraphOrDefault(count); + string text = p.Text; + if (next != null && Math.Abs(next.StartTime.TotalMilliseconds - p.EndTime.TotalMilliseconds) < 100) + sb.AppendLine(string.Format(paragraphWriteFormatOpen, EncodeTime(p.StartTime), text)); + else + sb.AppendLine(string.Format(paragraphWriteFormat, EncodeTime(p.StartTime), EncodeTime(p.EndTime), text)); + count++; + } + sb.AppendLine("\t
"); + sb.AppendLine(""); + sb.AppendLine(""); + return sb.ToString().Trim(); + } + + private static string EncodeTime(TimeCode time) + { + //3:15:22 + if (time.Hours > 0) + return string.Format("{0:00}:{1:00}:{2:00}.{3:00}", time.Hours, time.Minutes, time.Seconds, time.Milliseconds / 10); + if (time.Minutes > 9) + return string.Format("{0:00}:{1:00}.{2:00}", time.Minutes, time.Seconds, time.Milliseconds / 10); + return string.Format("{0}:{1:00}.{2:00}", time.Minutes, time.Seconds, time.Milliseconds / 10); + } + + private static TimeCode DecodeTimeCode(string[] s) + { + if (s.Length == 3) + return new TimeCode(0, int.Parse(s[0]), int.Parse(s[1]), int.Parse(s[2]) * 10); + return new TimeCode(int.Parse(s[0]), int.Parse(s[1]), int.Parse(s[2]), int.Parse(s[3]) * 10); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + var sb = new StringBuilder(); + foreach (string l in lines) + sb.AppendLine(l); + string allInput = sb.ToString(); + string allInputLower = allInput.ToLower(); + const string syncTag = "

= 0) + { + var syncEndPos = allInputLower.IndexOf("

", index, StringComparison.Ordinal); + if (syncEndPos > 0) + { + string s = allInput.Substring(syncStartPos + 2, syncEndPos - syncStartPos - 2); + int indexOfBegin = s.IndexOf(" data-begin=", StringComparison.Ordinal); + int indexOfAttributesEnd = s.IndexOf('>'); + if (indexOfBegin >= 0 && indexOfAttributesEnd > indexOfBegin) + { + string text = s.Substring(indexOfAttributesEnd + 1).Trim(); + text = text.Replace("
", Environment.NewLine); + text = text.Replace("
", Environment.NewLine); + text = text.Replace("
", Environment.NewLine); + text = text.Replace("\t", " "); + while (text.Contains(" ")) + text = text.Replace(" ", " "); + while (text.Contains(Environment.NewLine + " ")) + text = text.Replace(Environment.NewLine + " ", Environment.NewLine); + while (text.Contains(Environment.NewLine + Environment.NewLine)) + text = text.Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine); + + string begin = s.Substring(indexOfBegin + " data-begin=".Length); + tcBegin.Clear(); + for (int i = 0; i <= 10; i++) + { + if (begin.Length > i && @"0123456789:.".Contains(begin[i])) + { + tcBegin.Append(begin[i]); + } + } + + tcEnd.Clear(); + var indexOfEnd = s.IndexOf(" data-end=", StringComparison.Ordinal); + if (indexOfEnd >= 0) + { + string end = s.Substring(indexOfEnd + " data-end=".Length); + for (int i = 0; i <= 10; i++) + { + if (end.Length > i && @"0123456789:.".Contains(end[i])) + { + tcEnd.Append(end[i]); + } + } + } + + var arr = tcBegin.ToString().Split(splitChars, StringSplitOptions.RemoveEmptyEntries); + if (arr.Length == 3 || arr.Length == 4) + { + var p = new Paragraph(); + p.Text = text; + try + { + p.StartTime = DecodeTimeCode(arr); + if (tcEnd.Length > 0) + { + arr = tcEnd.ToString().Split(splitChars, StringSplitOptions.RemoveEmptyEntries); + p.EndTime = DecodeTimeCode(arr); + } + subtitle.Paragraphs.Add(p); + } + catch + { + _errorCount++; + } + } + } + } + if (syncEndPos <= 0) + { + syncStartPos = -1; + } + else + { + syncStartPos = allInputLower.IndexOf(syncTag, syncEndPos, StringComparison.Ordinal); + index = syncStartPos + syncTag.Length; + } + } + + index = 1; + foreach (Paragraph paragraph in subtitle.Paragraphs) + { + Paragraph next = subtitle.GetParagraphOrDefault(index); + if (next != null) + { + paragraph.EndTime.TotalMilliseconds = next.StartTime.TotalMilliseconds - 1; + } + else if (paragraph.EndTime.TotalMilliseconds < 50) + { + paragraph.EndTime.TotalMilliseconds = paragraph.StartTime.TotalMilliseconds + Utilities.GetOptimalDisplayMilliseconds(paragraph.Text); + } + if (paragraph.Duration.TotalMilliseconds > Configuration.Settings.General.SubtitleMaximumDisplayMilliseconds) + { + paragraph.EndTime.TotalMilliseconds = paragraph.StartTime.TotalMilliseconds + Utilities.GetOptimalDisplayMilliseconds(paragraph.Text); + } + index++; + } + + foreach (Paragraph p2 in subtitle.Paragraphs) + p2.Text = WebUtility.HtmlDecode(p2.Text); + + subtitle.Renumber(); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/SoftNiColonSub.cs b/libse/SubtitleFormats/SoftNiColonSub.cs new file mode 100644 index 000000000..d8671965a --- /dev/null +++ b/libse/SubtitleFormats/SoftNiColonSub.cs @@ -0,0 +1,243 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + /// + /// SoftNi - http://www.softni.com/ + /// + public class SoftNicolonSub : SubtitleFormat + { + private static readonly Regex regexTimeCodes = new Regex(@"^\d\d:\d\d:\d\d:\d\d\\\d\d:\d\d:\d\d:\d\d$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".sub"; } + } + + public override string Name + { + get { return "SoftNi colon sub"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + DoLoadSubtitle(subtitle, lines); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + var lineSb = new StringBuilder(); + sb.AppendLine("*PART 1*"); + sb.AppendLine("00:00:00:00\\00:00:00:00"); + foreach (Paragraph p in subtitle.Paragraphs) + { + string text = p.Text; + bool positionTop = false; + + // If text starts with {\an8}, subtitle appears at the top + if (text.StartsWith("{\\an8}", StringComparison.Ordinal)) + { + positionTop = true; + // Remove the tag {\an8}. + text = text.Remove(0, 6); + } + + // Split lines (split a subtitle into its lines) + var lines = text.SplitToLines(); + int count = 0; + lineSb.Clear(); + string tempLine = string.Empty; + bool nextLineInItalics = false; + foreach (string line in lines) + { + // Append line break in every line except the first one + if (count > 0) + lineSb.Append(Environment.NewLine); + + tempLine = line; + + // This line should be in italics (it was detected in previous line) + if (nextLineInItalics) + { + tempLine = "" + tempLine; + nextLineInItalics = false; + } + + if (tempLine.StartsWith("") && tempLine.EndsWith("")) + { + // Whole line is in italics + // Remove from the beginning + tempLine = tempLine.Remove(0, 3); + // Remove from the end + tempLine = tempLine.Remove(tempLine.Length - 4, 4); + // Add new italics tag at the beginning + tempLine = "[" + tempLine; + } + else if (tempLine.StartsWith("") && Utilities.CountTagInText(tempLine, "") > Utilities.CountTagInText(tempLine, "")) + { + // Line starts with but italics are not closed. So the next line should be in italics + nextLineInItalics = true; + } + lineSb.Append(tempLine); + count++; + + text = lineSb.ToString(); + // Replace remaining italics tags + text = text.Replace("", @"["); + text = text.Replace("", @"]"); + text = HtmlUtil.RemoveHtmlTags(text); + } + + // Add top-position SoftNI marker "}" at the beginning of first line. + if (positionTop) + text = "}" + text; + + sb.AppendLine(string.Format("{0}{1}{2}\\{3}", text, Environment.NewLine, p.StartTime.ToHHMMSSPeriodFF().Replace(".", ":"), p.EndTime.ToHHMMSSPeriodFF().Replace(".", ":"))); + } + sb.AppendLine(@"*END*"); + sb.AppendLine(@"...........\..........."); + sb.AppendLine(@"*CODE*"); + sb.AppendLine(@"0000000000000000"); + sb.AppendLine(@"*CAST*"); + sb.AppendLine(@"*GENERATOR*"); + sb.AppendLine(@"*FONTS*"); + sb.AppendLine(@"*READ*"); + sb.AppendLine(@"0,300 15,000 130,000 100,000 25,000"); + sb.AppendLine(@"*TIMING*"); + sb.AppendLine(@"1 25 0"); + sb.AppendLine(@"*TIMED BACKUP NAME*"); + sb.AppendLine(@"C:\"); + sb.AppendLine(@"*FORMAT SAMPLE ÅåÉéÌìÕõÛûÿ*"); + sb.AppendLine(@"*READ ADVANCED*"); + sb.AppendLine(@"< > 1 1 0,300"); + sb.AppendLine(@"*MARKERS*"); + return sb.ToString(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + DoLoadSubtitle(subtitle, lines); + } + + private void DoLoadSubtitle(Subtitle subtitle, List lines) + { + //—Peter. + //—Estoy de licencia. + //01:48:50.07\01:48:52.01 + var sb = new StringBuilder(); + var lineSb = new StringBuilder(); + Paragraph p = null; + subtitle.Paragraphs.Clear(); + char[] splitChars = { ':', '.' }; + foreach (string line in lines) + { + string s = line.Trim(); + if (regexTimeCodes.IsMatch(s)) + { + // Start and end time separated by "\" + var temp = s.Split('\\'); + if (temp.Length > 1) + { + string start = temp[0]; + string end = temp[1]; + + string[] startParts = start.Split(splitChars, StringSplitOptions.RemoveEmptyEntries); + string[] endParts = end.Split(splitChars, StringSplitOptions.RemoveEmptyEntries); + if (startParts.Length == 4 && endParts.Length == 4) + { + try + { + p = new Paragraph(); + p.StartTime = DecodeTimeCode(startParts); + p.EndTime = DecodeTimeCode(endParts); + string text = sb.ToString().Trim(); + + bool positionTop = false; + // If text starts with "}", subtitle appears at the top + if (text.StartsWith('}')) + { + positionTop = true; + // Remove the tag "}" + text = text.Remove(0, 1); + } + // Replace tags + text = text.Replace("[", @""); + text = text.Replace("]", @""); + + // Split subtitle lines (one subtitle has one or more lines) + var subtitleLines = text.SplitToLines(); + int count = 0; + lineSb.Clear(); + string tempLine = string.Empty; + foreach (string subtitleLine in subtitleLines) + { + // Append line break in every line except the first one + if (count > 0) + lineSb.Append(Environment.NewLine); + tempLine = subtitleLine; + // Close italics in every line (if next line is in italics, SoftNI will use "[" at the beginning) + if (Utilities.CountTagInText(tempLine, "") > Utilities.CountTagInText(tempLine, "")) + tempLine = tempLine + ""; + + lineSb.Append(tempLine); + count++; + } + text = lineSb.ToString(); + + // Replace "line break" with just a line break (SubRip does not need to close italics and open them again in the next line). + text = text.Replace("" + Environment.NewLine + "", Environment.NewLine); + + // Subtitle appears at the top (add tag) + if (positionTop) + text = "{\\an8}" + text; + + p.Text = text; + if (text.Length > 0) + subtitle.Paragraphs.Add(p); + sb.Clear(); + } + catch (Exception exception) + { + _errorCount++; + System.Diagnostics.Debug.WriteLine(exception.Message); + } + } + } + } + else if (string.IsNullOrWhiteSpace(line) || line.StartsWith('*')) + { + // skip empty lines or start + } + else if (p != null) + { + sb.AppendLine(line); + } + } + + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + //00:00:07:12 + var hour = int.Parse(parts[0]); + var minutes = int.Parse(parts[1]); + var seconds = int.Parse(parts[2]); + var frames = int.Parse(parts[3]); + + return new TimeCode(hour, minutes, seconds, FramesToMillisecondsMax999(frames)); + } + + } +} diff --git a/libse/SubtitleFormats/SoftNiSub.cs b/libse/SubtitleFormats/SoftNiSub.cs new file mode 100644 index 000000000..4990c833b --- /dev/null +++ b/libse/SubtitleFormats/SoftNiSub.cs @@ -0,0 +1,244 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + /// + /// SoftNi - http://www.softni.com/ + /// + public class SoftNiSub : SubtitleFormat + { + private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d:\d\d:\d\d\.\d\d\\\d\d:\d\d:\d\d\.\d\d$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".sub"; } + } + + public override string Name + { + get { return "SoftNi sub"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + DoLoadSubtitle(subtitle, lines); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + var lineSb = new StringBuilder(); + sb.AppendLine("*PART 1*"); + sb.AppendLine("00:00:00.00\\00:00:00.00"); + const string writeFormat = "{0}{1}{2}\\{3}"; + foreach (Paragraph p in subtitle.Paragraphs) + { + string text = p.Text; + bool positionTop = false; + + // If text starts with {\an8}, subtitle appears at the top + if (text.StartsWith("{\\an8}", StringComparison.Ordinal)) + { + positionTop = true; + // Remove the tag {\an8}. + text = text.Remove(0, 6); + } + + // Split lines (split a subtitle into its lines) + var lines = text.SplitToLines(); + int count = 0; + lineSb.Clear(); + string tempLine = string.Empty; + bool nextLineInItalics = false; + foreach (string line in lines) + { + // Append line break in every line except the first one + if (count > 0) + lineSb.Append(Environment.NewLine); + + tempLine = line; + + // This line should be in italics (it was detected in previous line) + if (nextLineInItalics) + { + tempLine = "" + tempLine; + nextLineInItalics = false; + } + + if (tempLine.StartsWith("") && tempLine.EndsWith("")) + { + // Whole line is in italics + // Remove from the beginning + tempLine = tempLine.Remove(0, 3); + // Remove from the end + tempLine = tempLine.Remove(tempLine.Length - 4, 4); + // Add new italics tag at the beginning + tempLine = "[" + tempLine; + } + else if (tempLine.StartsWith("", StringComparison.Ordinal) && Utilities.CountTagInText(tempLine, "") > Utilities.CountTagInText(tempLine, "")) + { + // Line starts with but italics are not closed. So the next line should be in italics + nextLineInItalics = true; + } + lineSb.Append(tempLine); + count++; + + text = lineSb.ToString(); + // Replace remaining italics tags + text = text.Replace("", @"["); + text = text.Replace("", @"]"); + text = HtmlUtil.RemoveHtmlTags(text); + } + + // Add top-position SoftNI marker "}" at the beginning of first line. + if (positionTop) + text = "}" + text; + + sb.AppendLine(string.Format(writeFormat, text, Environment.NewLine, p.StartTime.ToHHMMSSPeriodFF(), p.EndTime.ToHHMMSSPeriodFF())); + } + sb.AppendLine(@"*END*"); + sb.AppendLine(@"...........\..........."); + sb.AppendLine(@"*CODE*"); + sb.AppendLine(@"0000000000000000"); + sb.AppendLine(@"*CAST*"); + sb.AppendLine(@"*GENERATOR*"); + sb.AppendLine(@"*FONTS*"); + sb.AppendLine(@"*READ*"); + sb.AppendLine(@"0,300 15,000 130,000 100,000 25,000"); + sb.AppendLine(@"*TIMING*"); + sb.AppendLine(@"1 25 0"); + sb.AppendLine(@"*TIMED BACKUP NAME*"); + sb.AppendLine(@"C:\"); + sb.AppendLine(@"*FORMAT SAMPLE ÅåÉéÌìÕõÛûÿ*"); + sb.AppendLine(@"*READ ADVANCED*"); + sb.AppendLine(@"< > 1 1 0,300"); + sb.AppendLine(@"*MARKERS*"); + return sb.ToString(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + DoLoadSubtitle(subtitle, lines); + } + + private void DoLoadSubtitle(Subtitle subtitle, List lines) + { + //—Peter. + //—Estoy de licencia. + //01:48:50.07\01:48:52.01 + var sb = new StringBuilder(); + var lineSb = new StringBuilder(); + Paragraph p = null; + subtitle.Paragraphs.Clear(); + char[] splitChars = { ':', '.' }; + foreach (string line in lines) + { + string s = line.Trim(); + if (RegexTimeCodes.IsMatch(s)) + { + // Start and end time separated by "\" + var temp = s.Split('\\'); + if (temp.Length > 1) + { + string start = temp[0]; + string end = temp[1]; + + string[] startParts = start.Split(splitChars, StringSplitOptions.RemoveEmptyEntries); + string[] endParts = end.Split(splitChars, StringSplitOptions.RemoveEmptyEntries); + if (startParts.Length == 4 && endParts.Length == 4) + { + try + { + p = new Paragraph(); + p.StartTime = DecodeTimeCode(startParts); + p.EndTime = DecodeTimeCode(endParts); + string text = sb.ToString().Trim(); + + bool positionTop = false; + // If text starts with "}", subtitle appears at the top + if (text.StartsWith('}')) + { + positionTop = true; + // Remove the tag "{" + text = text.Remove(0, 1); + } + // Replace tags + text = text.Replace("[", @""); + text = text.Replace("]", @""); + + // Split subtitle lines (one subtitle has one or more lines) + var subtitleLines = text.SplitToLines(); + int count = 0; + lineSb.Clear(); + string tempLine = string.Empty; + foreach (string subtitleLine in subtitleLines) + { + // Append line break in every line except the first one + if (count > 0) + lineSb.Append(Environment.NewLine); + tempLine = subtitleLine; + // Close italics in every line (if next line is in italics, SoftNI will use "[" at the beginning) + if (Utilities.CountTagInText(tempLine, "") > Utilities.CountTagInText(tempLine, "")) + tempLine = tempLine + ""; + + lineSb.Append(tempLine); + count++; + } + text = lineSb.ToString(); + + // Replace "line break" with just a line break (SubRip does not need to close italics and open them again in the next line). + text = text.Replace("" + Environment.NewLine + "", Environment.NewLine); + + // Subtitle appears at the top (add tag) + if (positionTop) + text = "{\\an8}" + text; + + p.Text = text; + if (text.Length > 0) + subtitle.Paragraphs.Add(p); + sb.Clear(); + } + catch (Exception exception) + { + _errorCount++; + System.Diagnostics.Debug.WriteLine(exception.Message); + } + } + } + } + else if (string.IsNullOrWhiteSpace(line) || line.StartsWith('*')) + { + // skip empty lines + } + else if (p != null) + { + sb.AppendLine(line); + } + } + + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + //00:00:07:12 + var hour = int.Parse(parts[0]); + var minutes = int.Parse(parts[1]); + var seconds = int.Parse(parts[2]); + var frames = int.Parse(parts[3]); + + return new TimeCode(hour, minutes, seconds, FramesToMillisecondsMax999(frames)); + } + + } +} diff --git a/libse/SubtitleFormats/Son.cs b/libse/SubtitleFormats/Son.cs new file mode 100644 index 000000000..bfb2979e0 --- /dev/null +++ b/libse/SubtitleFormats/Son.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class Son : SubtitleFormat + { + public override string Extension + { + get { return ".son"; } + } + + public override string Name + { + get { return "SON"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + + var sb = new StringBuilder(); + foreach (string line in lines) + sb.AppendLine(line); + + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + int index = 0; + const string writeFormat = "{0:0000}\t{1}\t{2}\t{3}"; + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.AppendLine(string.Format(writeFormat, index + 1, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), HtmlUtil.RemoveHtmlTags(p.Text).Replace(Environment.NewLine, "\t"))); + index++; + } + return sb.ToString(); + } + + private static string EncodeTimeCode(TimeCode time) + { + //00:03:15:22 (last is frame) + return time.ToHHMMSSFF(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + //0001 00:00:19:13 00:00:22:10 a_0001.tif + Paragraph p = null; + subtitle.Paragraphs.Clear(); + _errorCount = 0; + var regexTimeCodes = new Regex(@"^\d\d\d\d[\t]+\d\d:\d\d:\d\d:\d\d\t\d\d:\d\d:\d\d:\d\d\t.+\.(tif|tiff|png|bmp|TIF|TIFF|PNG|BMP)", RegexOptions.Compiled); + int index = 0; + char[] splitChar = { ':' }; + foreach (string line in lines) + { + if (regexTimeCodes.IsMatch(line)) + { + string temp = line.Substring(0, regexTimeCodes.Match(line).Length); + string start = temp.Substring(5, 11); + string end = temp.Substring(12 + 5, 11); + + string[] startParts = start.Split(splitChar, StringSplitOptions.RemoveEmptyEntries); + string[] endParts = end.Split(splitChar, StringSplitOptions.RemoveEmptyEntries); + if (startParts.Length == 4 && endParts.Length == 4) + { + int lastIndexOfTab = line.LastIndexOf('\t'); + string text = line.Remove(0, lastIndexOfTab + 1).Trim(); + if (!text.Contains(Environment.NewLine)) + text = text.Replace("\t", Environment.NewLine); + p = new Paragraph(DecodeTimeCode(startParts), DecodeTimeCode(endParts), text); + subtitle.Paragraphs.Add(p); + } + } + else if (string.IsNullOrWhiteSpace(line) || line.StartsWith("Display_Area") || line.StartsWith('#') || line.StartsWith("Color") || index < 10) + { + // skip these lines + } + else if (p != null) + { + _errorCount++; + } + index++; + } + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + //00:00:07:12 + var hour = int.Parse(parts[0]); + var minutes = int.Parse(parts[1]); + var seconds = int.Parse(parts[2]); + var frames = int.Parse(parts[3]); + + return new TimeCode(hour, minutes, seconds, FramesToMillisecondsMax999(frames)); + } + + } +} diff --git a/libse/SubtitleFormats/SonicScenaristBitmaps.cs b/libse/SubtitleFormats/SonicScenaristBitmaps.cs new file mode 100644 index 000000000..0eaf85ec0 --- /dev/null +++ b/libse/SubtitleFormats/SonicScenaristBitmaps.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class SonicScenaristBitmaps : SubtitleFormat + { + private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d\d\d\s+\d\d:\d\d:\d\d:\d\d\s+\d\d:\d\d:\d\d:\d\d\s+.+\.(tif|tiff|png|bmp|TIF|TIFF|PNG|BMP)", RegexOptions.Compiled); + + public override string Extension + { + get { return ".sst"; } + } + + public override string Name + { + get { return "Sonic Scenarist Bitmaps"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + if (System.IO.Path.GetExtension(fileName).ToLowerInvariant() != ".sst") + return false; + + var subtitle = new Subtitle(); + var sb = new StringBuilder(); + foreach (string line in lines) + sb.AppendLine(line); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + int index = 0; + const string writeFormat = "{0:0000} {1} {2} {3}"; + foreach (Paragraph p in subtitle.Paragraphs) + { + //0001 00:49:26:22 00:49:27:13 t01_v001c001_22_0001.bmp + sb.AppendLine(string.Format(writeFormat, index + 1, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), HtmlUtil.RemoveHtmlTags(p.Text).Replace(Environment.NewLine, "\t"))); + index++; + } + return sb.ToString(); + } + + private static string EncodeTimeCode(TimeCode time) + { + //00:03:15:22 (last is frame) + return time.ToHHMMSSFF(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + //00:03:15:22 00:03:23:10 This is line one. + //This is line two. + Paragraph p = null; + subtitle.Paragraphs.Clear(); + _errorCount = 0; + int index = 0; + char[] splitChar = { ' ' }; + var sb = new StringBuilder(); + foreach (string line in lines) + { + if (RegexTimeCodes.IsMatch(line)) + { + string temp = line.Substring(0, RegexTimeCodes.Match(line).Length); + + string[] parts = temp.Split(splitChar, StringSplitOptions.RemoveEmptyEntries); + + if (parts.Length > 3) + { + sb.Clear(); + for (int i = 3; i < parts.Length; i++) + sb.Append(parts[i] + " "); + string text = sb.ToString().Trim(); + p = new Paragraph(DecodeTimeCode(parts[1].Split(':')), DecodeTimeCode(parts[2].Split(':')), text); + subtitle.Paragraphs.Add(p); + } + } + else if (string.IsNullOrWhiteSpace(line) || line.StartsWith("Display_Area", StringComparison.Ordinal) || line.StartsWith('#') || line.StartsWith("Color", StringComparison.Ordinal) || index < 10) + { + // skip these lines + } + else if (p != null) + { + _errorCount++; + } + index++; + } + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + //00:00:07:12 + var hour = int.Parse(parts[0]); + var minutes = int.Parse(parts[1]); + var seconds = int.Parse(parts[2]); + var frames = double.Parse(parts[3]); + + int milliseconds = FramesToMillisecondsMax999(frames); + if (milliseconds > 999) + milliseconds = 999; + return new TimeCode(hour, minutes, seconds, milliseconds); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/SonyDVDArchitect.cs b/libse/SubtitleFormats/SonyDVDArchitect.cs new file mode 100644 index 000000000..0675b6654 --- /dev/null +++ b/libse/SubtitleFormats/SonyDVDArchitect.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class SonyDVDArchitect : SubtitleFormat + { + private static readonly Regex Regex = new Regex(@"^\d\d:\d\d:\d\d:\d\d[ ]+-[ ]+\d\d:\d\d:\d\d:\d\d", RegexOptions.Compiled); + + public override string Extension + { + get { return ".sub"; } + } + + public override string Name + { + get { return "Sony DVDArchitect"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + const string writeFormat = "{0:00}:{1:00}:{2:00}:{3:00} - {4:00}:{5:00}:{6:00}:{7:00} \t{8}"; + foreach (Paragraph p in subtitle.Paragraphs) + { + var text = HtmlUtil.RemoveHtmlTags(p.Text, true); + text = text.Replace(Environment.NewLine, "\r"); + sb.AppendLine(string.Format(writeFormat, p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, p.StartTime.Milliseconds / 10, + p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, p.EndTime.Milliseconds / 10, + text)); + } + return sb.ToString().Trim() + Environment.NewLine + Environment.NewLine + Environment.NewLine; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + // 00:04:10:92 - 00:04:13:32 Raise Yourself To Help Mankind + // 00:04:27:92 - 00:04:30:92 الجهة المتولية للمسئولية الاجتماعية لشركتنا. + + _errorCount = 0; + Paragraph lastParagraph = null; + foreach (string line in lines) + { + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } + + bool success = false; + + Match match = null; + if (line.Length > 26 && line[2] == ':') + match = Regex.Match(line); + + if (match != null && match.Success) + { + string s = line.Substring(0, match.Length); + s = s.Replace(" - ", ":"); + s = s.Replace(" ", string.Empty); + string[] parts = s.Split(':'); + if (parts.Length == 8) + { + int hours = int.Parse(parts[0]); + int minutes = int.Parse(parts[1]); + int seconds = int.Parse(parts[2]); + int milliseconds = int.Parse(parts[3]) * 10; + var start = new TimeCode(hours, minutes, seconds, milliseconds); + + hours = int.Parse(parts[4]); + minutes = int.Parse(parts[5]); + seconds = int.Parse(parts[6]); + milliseconds = int.Parse(parts[7]) * 10; + var end = new TimeCode(hours, minutes, seconds, milliseconds); + + string text = line.Substring(match.Length).TrimStart(); + text = text.Replace("|", Environment.NewLine); + + lastParagraph = new Paragraph(start, end, text); + subtitle.Paragraphs.Add(lastParagraph); + success = true; + } + } + else if (lastParagraph != null && Utilities.GetNumberOfLines(lastParagraph.Text) < 5) + { + lastParagraph.Text += Environment.NewLine + line.Trim(); + success = true; + } + if (!success) + _errorCount++; + } + subtitle.Renumber(); + } + } +} diff --git a/libse/SubtitleFormats/SonyDVDArchitectExplicitDuration.cs b/libse/SubtitleFormats/SonyDVDArchitectExplicitDuration.cs new file mode 100644 index 000000000..5dbabb275 --- /dev/null +++ b/libse/SubtitleFormats/SonyDVDArchitectExplicitDuration.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class SonyDVDArchitectExplicitDuration : SubtitleFormat + { + private static readonly Regex regex = new Regex(@"^\d\d:\d\d:\d\d\.\d\d\d[ \t]+\d\d:\d\d:\d\d\.\d\d\d[ \t]+\d\d:\d\d:\d\d\.\d\d\d[ \t]+", RegexOptions.Compiled); + + public override string Extension + { + get { return ".sub"; } + } + + public override string Name + { + get { return "Sony DVDArchitect Explicit duration"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + const string writeFormat = "{0:00}:{1:00}:{2:00}.{3:000}\t{4:00}:{5:00}:{6:00}.{7:000}\t{8:00}:{9:00}:{10:00}.{11:000}\t{12}"; + foreach (Paragraph p in subtitle.Paragraphs) + { + string text = HtmlUtil.RemoveHtmlTags(p.Text); + text = text.Replace(Environment.NewLine, "\r"); + sb.AppendLine(string.Format(writeFormat, p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, p.StartTime.Milliseconds, + p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, p.EndTime.Milliseconds, + p.Duration.Hours, p.Duration.Minutes, p.Duration.Seconds, p.Duration.Milliseconds, + text)); + } + return sb.ToString().Trim() + Environment.NewLine + Environment.NewLine + Environment.NewLine; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { //00:02:10.354 00:02:13.047 00:00:02.693 Her er endnu en tekstfil fra DVD-Architect.Med 2 linier. + //00:02:14.018 00:02:19.018 00:00:05.000 - Prøvetekst 2- Linie 2 + //newline = \r (0D) + + _errorCount = 0; + Paragraph lastParagraph = null; + foreach (string line in lines) + { + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } + + string s = line; + bool success = false; + bool isTimeCode = false; + + if (s.Length > 26 && s.IndexOf(':') == 2) + { + var match = regex.Match(s); + if (match.Success) + { + isTimeCode = true; + s = s.Substring(0, match.Length); + s = s.Replace('\t', ':'); + s = s.Replace('.', ':'); + s = s.Replace(" ", string.Empty); + s = s.Trim().TrimEnd(':').TrimEnd(); + string[] parts = s.Split(':'); + if (parts.Length == 12) + { + int hours = int.Parse(parts[0]); + int minutes = int.Parse(parts[1]); + int seconds = int.Parse(parts[2]); + int milliseconds = int.Parse(parts[3]); + var start = new TimeCode(hours, minutes, seconds, milliseconds); + + hours = int.Parse(parts[4]); + minutes = int.Parse(parts[5]); + seconds = int.Parse(parts[6]); + milliseconds = int.Parse(parts[7]); + var end = new TimeCode(hours, minutes, seconds, milliseconds); + + string text = line.Substring(match.Length).TrimStart(); + text = text.Replace("|", Environment.NewLine); + + lastParagraph = new Paragraph(start, end, text); + subtitle.Paragraphs.Add(lastParagraph); + success = true; + } + } + } + if (!isTimeCode && lastParagraph != null && Utilities.GetNumberOfLines(lastParagraph.Text) < 5) + { + lastParagraph.Text += Environment.NewLine + line.Trim(); + success = true; + } + if (!success) + _errorCount++; + } + subtitle.Renumber(); + } + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/SonyDVDArchitectLineAndDuration.cs b/libse/SubtitleFormats/SonyDVDArchitectLineAndDuration.cs new file mode 100644 index 000000000..29c852025 --- /dev/null +++ b/libse/SubtitleFormats/SonyDVDArchitectLineAndDuration.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class SonyDVDArchitectLineAndDuration : SubtitleFormat + { + private static readonly Regex Regex = new Regex(@"^\d+\t\d\d:\d\d:\d\d:\d\d\t\d\d:\d\d:\d\d:\d\d\t\d\d:\d\d:\d\d:\d\d$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Sony DVDArchitect line/duration"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + sb.AppendLine("Title: " + title); + sb.AppendLine("Translator: No Author"); + sb.AppendLine("Date: " + DateTime.Now.ToString("dd-MM-yyyy").Replace("-", ".")); // 25.08.2011 + double milliseconds = 0; + if (subtitle.Paragraphs.Count > 0) + milliseconds = subtitle.Paragraphs[subtitle.Paragraphs.Count - 1].EndTime.TotalMilliseconds; + var tc = new TimeCode(milliseconds); + sb.AppendLine(string.Format("Duration: {0:00}:{1:00}:{2:00}:{3:00}", tc.Hours, tc.Minutes, tc.Seconds, MillisecondsToFramesMaxFrameRate(tc.Milliseconds))); // 01:20:49:12 + sb.AppendLine("Program start: 00:00:00:00"); + sb.AppendLine("Title count: " + subtitle.Paragraphs.Count); + sb.AppendLine(); + sb.AppendLine("#\tIn\tOut\tDuration"); + sb.AppendLine(); + int count = 0; + const string writeFormat = "{13}\t{0:00}:{1:00}:{2:00}:{3:00}\t{4:00}:{5:00}:{6:00}:{7:00}\t{8:00}:{9:00}:{10:00}:{11:00}\r\n{12}"; + foreach (Paragraph p in subtitle.Paragraphs) + { + count++; + var text = HtmlUtil.RemoveHtmlTags(p.Text, true); + + // to avoid rounding errors in duration + var startFrame = MillisecondsToFramesMaxFrameRate(p.StartTime.Milliseconds); + var endFrame = MillisecondsToFramesMaxFrameRate(p.EndTime.Milliseconds); + var durationCalc = new Paragraph( + new TimeCode(p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, FramesToMillisecondsMax999(startFrame)), + new TimeCode(p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, FramesToMillisecondsMax999(endFrame)), + string.Empty); + + sb.AppendLine(string.Format(writeFormat + Environment.NewLine, + p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, startFrame, + p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, endFrame, + durationCalc.Duration.Hours, durationCalc.Duration.Minutes, durationCalc.Duration.Seconds, MillisecondsToFramesMaxFrameRate(durationCalc.Duration.Milliseconds), + text, count)); + } + return sb.ToString().Trim() + Environment.NewLine + Environment.NewLine + Environment.NewLine; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { //22 00:04:19:12 00:04:21:09 00:00:01:21 + _errorCount = 0; + Paragraph lastParagraph = null; + int count = 0; + foreach (string line in lines) + { + bool isTimeCode = false; + if (line.Length > 0) + { + bool success = false; + if (line.Length > 31 && line.IndexOf(':') > 1) + { + var match = Regex.Match(line); + if (match.Success) + { + isTimeCode = true; + if (lastParagraph != null) + subtitle.Paragraphs.Add(lastParagraph); + + var arr = line.Split('\t'); + TimeCode start = DecodeTimeCode(arr[1]); + TimeCode end = DecodeTimeCode(arr[2]); + lastParagraph = new Paragraph(start, end, string.Empty); + success = true; + } + } + if (!isTimeCode && !string.IsNullOrWhiteSpace(line) && lastParagraph != null && Utilities.GetNumberOfLines(lastParagraph.Text) < 5) + { + lastParagraph.Text = (lastParagraph.Text + Environment.NewLine + line).Trim(); + success = true; + } + if (!success && count > 9) + _errorCount++; + } + count++; + } + if (lastParagraph != null) + subtitle.Paragraphs.Add(lastParagraph); + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string s) + { + var parts = s.Split(':'); + + //00:00:07:12 + string hour = parts[0]; + string minutes = parts[1]; + string seconds = parts[2]; + string frames = parts[3]; + + TimeCode tc = new TimeCode(int.Parse(hour), int.Parse(minutes), int.Parse(seconds), FramesToMillisecondsMax999(int.Parse(frames))); + return tc; + } + + } +} diff --git a/libse/SubtitleFormats/SonyDVDArchitectTabs.cs b/libse/SubtitleFormats/SonyDVDArchitectTabs.cs new file mode 100644 index 000000000..f58f53cdc --- /dev/null +++ b/libse/SubtitleFormats/SonyDVDArchitectTabs.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class SonyDVDArchitectTabs : SubtitleFormat + { + private static readonly Regex Regex = new Regex(@"^\d\d:\d\d:\d\d:\d\d[ \t]+\d\d:\d\d:\d\d:\d\d[ \t]+", RegexOptions.Compiled); + + public override string Extension + { + get { return ".sub"; } + } + + public override string Name + { + get { return "Sony DVDArchitect Tabs"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + const string writeFormat = "{0:00}:{1:00}:{2:00}:{3:00}\t{4:00}:{5:00}:{6:00}:{7:00}\t{8}"; + foreach (Paragraph p in subtitle.Paragraphs) + { + string text = HtmlUtil.RemoveHtmlTags(p.Text); + text = text.Replace(Environment.NewLine, "\r"); + sb.AppendLine(string.Format(writeFormat, p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, p.StartTime.Milliseconds / 10, + p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, p.EndTime.Milliseconds / 10, + text)); + } + return sb.ToString().Trim() + Environment.NewLine + Environment.NewLine + Environment.NewLine; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { //00:02:09:34 00:02:13:07 - Hvad mener du så om konkurrencen?- Jo, det er helt fint. + //00:02:14:02 00:02:16:41 - Var det den rigtige der vandt?- Ja, bestemt. + //newline = \r (0D) + + _errorCount = 0; + Paragraph lastParagraph = null; + foreach (string line in lines) + { + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } + + bool success = false; + bool isTimeCode = false; + if (line.Length > 26 && line.IndexOf(':') == 2) + { + var match = Regex.Match(line); + if (match.Success) + { + isTimeCode = true; + string s = line.Substring(0, match.Length); + s = s.Replace('\t', ':'); + s = s.Replace(" ", string.Empty); + s = s.Trim().TrimEnd(':').TrimEnd(); + string[] parts = s.Split(':'); + if (parts.Length == 8) + { + int hours = int.Parse(parts[0]); + int minutes = int.Parse(parts[1]); + int seconds = int.Parse(parts[2]); + int milliseconds = int.Parse(parts[3]) * 10; + var start = new TimeCode(hours, minutes, seconds, milliseconds); + + hours = int.Parse(parts[4]); + minutes = int.Parse(parts[5]); + seconds = int.Parse(parts[6]); + milliseconds = int.Parse(parts[7]) * 10; + var end = new TimeCode(hours, minutes, seconds, milliseconds); + + string text = line.Substring(match.Length).TrimStart(); + text = text.Replace("|", Environment.NewLine); + + lastParagraph = new Paragraph(start, end, text); + subtitle.Paragraphs.Add(lastParagraph); + success = true; + } + } + } + if (!isTimeCode && lastParagraph != null && Utilities.GetNumberOfLines(lastParagraph.Text) < 5) + { + lastParagraph.Text += Environment.NewLine + line.Trim(); + success = true; + } + if (!success) + _errorCount++; + } + subtitle.Renumber(); + } + } +} diff --git a/libse/SubtitleFormats/SonyDVDArchitectWithLineNumbers.cs b/libse/SubtitleFormats/SonyDVDArchitectWithLineNumbers.cs new file mode 100644 index 000000000..1b3c421c9 --- /dev/null +++ b/libse/SubtitleFormats/SonyDVDArchitectWithLineNumbers.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class SonyDVDArchitectWithLineNumbers : SubtitleFormat + { + private static readonly Regex RegexTimeCode = new Regex(@"^\d\d\d\d \d\d:\d\d:\d\d:\d\d \d\d:\d\d:\d\d:\d\d", RegexOptions.Compiled); + private static readonly Regex Regex1DigitMilliseconds = new Regex(@"^\d\d\d\d \d\d\d:\d\d:\d\d:\d \d\d\d:\d\d:\d\d:\d", RegexOptions.Compiled); + + public override string Extension + { + get { return ".sub"; } + } + + public override string Name + { + get { return "Sony DVDArchitect w. line#"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + string text = HtmlUtil.RemoveHtmlTags(p.Text); + text = text.Replace(Environment.NewLine, "\r"); + sb.AppendLine(string.Format("{9:0000} {0:00}:{1:00}:{2:00}:{3:00} {4:00}:{5:00}:{6:00}:{7:00} \t{8}" + Environment.NewLine, + p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, p.StartTime.Milliseconds / 10, + p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, p.EndTime.Milliseconds / 10, + text, p.Number)); + } + return sb.ToString().Trim() + Environment.NewLine + Environment.NewLine + Environment.NewLine; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { // 00:04:10:92 - 00:04:13:32 Raise Yourself To Help Mankind + // 00:04:27:92 - 00:04:30:92 الجهة المتولية للمسئولية الاجتماعية لشركتنا. + + _errorCount = 0; + Paragraph lastParagraph = null; + foreach (string line in lines) + { + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } + + bool success = false; + if (line.IndexOf(':') > 0) + { + string s = line; + var match = RegexTimeCode.Match(s); + var match1DigitMilliseconds = Regex1DigitMilliseconds.Match(s); + if (s.Length > 31 && match.Success) + { + s = s.Substring(5, match.Length - 5).TrimStart(); + s = s.Replace(" ", ":"); + s = s.Replace(" ", string.Empty); + string[] parts = s.Split(':'); + if (parts.Length == 8) + { + int hours = int.Parse(parts[0]); + int minutes = int.Parse(parts[1]); + int seconds = int.Parse(parts[2]); + int milliseconds = int.Parse(parts[3]) * 10; + var start = new TimeCode(hours, minutes, seconds, milliseconds); + + hours = int.Parse(parts[4]); + minutes = int.Parse(parts[5]); + seconds = int.Parse(parts[6]); + milliseconds = int.Parse(parts[7]) * 10; + var end = new TimeCode(hours, minutes, seconds, milliseconds); + + string text = line.Replace("\0", string.Empty).Substring(match.Length).TrimStart(); + text = text.Replace("|", Environment.NewLine); + + lastParagraph = new Paragraph(start, end, text); + subtitle.Paragraphs.Add(lastParagraph); + success = true; + } + } + else if (s.Length > 29 && match1DigitMilliseconds.Success) + { + s = s.Substring(5, match1DigitMilliseconds.Length - 5).TrimStart(); + s = s.Replace(" ", ":"); + s = s.Replace(" ", string.Empty); + string[] parts = s.Split(':'); + if (parts.Length == 8) + { + int hours = int.Parse(parts[0]); + int minutes = int.Parse(parts[1]); + int seconds = int.Parse(parts[2]); + int milliseconds = int.Parse(parts[3]) * 10; + var start = new TimeCode(hours, minutes, seconds, milliseconds); + + hours = int.Parse(parts[4]); + minutes = int.Parse(parts[5]); + seconds = int.Parse(parts[6]); + milliseconds = int.Parse(parts[7]) * 10; + var end = new TimeCode(hours, minutes, seconds, milliseconds); + + string text = line.Replace("\0", string.Empty).Substring(match1DigitMilliseconds.Length).TrimStart(); + text = text.Replace("|", Environment.NewLine); + + lastParagraph = new Paragraph(start, end, text); + subtitle.Paragraphs.Add(lastParagraph); + success = true; + } + } + else if (lastParagraph != null && Utilities.GetNumberOfLines(lastParagraph.Text) < 5) + { + lastParagraph.Text += Environment.NewLine + line.Trim(); + success = true; + } + } + else if (lastParagraph != null && Utilities.GetNumberOfLines(lastParagraph.Text) < 5) + { + lastParagraph.Text += Environment.NewLine + line.Trim(); + success = true; + } + if (!success) + _errorCount++; + } + subtitle.Renumber(); + } + } +} diff --git a/libse/SubtitleFormats/Spruce.cs b/libse/SubtitleFormats/Spruce.cs new file mode 100644 index 000000000..c37e5d481 --- /dev/null +++ b/libse/SubtitleFormats/Spruce.cs @@ -0,0 +1,211 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class Spruce : SubtitleFormat + { + private const string Italic = "^I"; + private const string Bold = "^B"; + private const string Underline = "^U"; + + public override string Extension + { + get { return ".stl"; } + } + + public override string Name + { + get { return "Spruce Subtitle File"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string Header = @"//Font select and font size +$FontName = Arial +$FontSize = 30 + +//Character attributes (global) +$Bold = FALSE +$UnderLined = FALSE +$Italic = FALSE + +//Position Control +$HorzAlign = Center +$VertAlign = Bottom +$XOffset = 0 +$YOffset = 0 + +//Contrast Control +$TextContrast = 15 +$Outline1Contrast = 8 +$Outline2Contrast = 15 +$BackgroundContrast = 0 + +//Effects Control +$ForceDisplay = FALSE +$FadeIn = 0 +$FadeOut = 0 + +//Other Controls +$TapeOffset = FALSE +//$SetFilePathToken = <<:>> + +//Colors +$ColorIndex1 = 0 +$ColorIndex2 = 1 +$ColorIndex3 = 2 +$ColorIndex4 = 3 + +//Subtitles"; + StringBuilder sb = new StringBuilder(); + sb.AppendLine(Header); + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.AppendLine(string.Format("{0},{1},{2}", EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), EncodeText(p.Text))); + } + return sb.ToString(); + } + + private static string EncodeText(string text) + { + text = HtmlUtil.FixUpperTags(text); + bool allItalic = text.StartsWith("") && text.EndsWith("") && Utilities.CountTagInText(text, "") == 1; + text = text.Replace("", Bold); + text = text.Replace("", Bold); + text = text.Replace("", Italic); + text = text.Replace("", Italic); + text = text.Replace("", Underline); + text = text.Replace("", Underline); + if (allItalic) + return text.Replace(Environment.NewLine, "|^I"); + return text.Replace(Environment.NewLine, "|"); + } + + private static string EncodeTimeCode(TimeCode time) + { + //00:01:54:19 + + int frames = time.Milliseconds / (1000 / 25); + + return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", time.Hours, time.Minutes, time.Seconds, frames); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + //00:01:54:19,00:01:56:17,We should be thankful|they accepted our offer. + _errorCount = 0; + subtitle.Paragraphs.Clear(); + var regexTimeCodes = new Regex(@"^\d\d:\d\d:\d\d:\d\d,\d\d:\d\d:\d\d:\d\d,.+", RegexOptions.Compiled); + if (fileName != null && fileName.EndsWith(".stl", StringComparison.OrdinalIgnoreCase)) // allow empty text if extension is ".stl"... + regexTimeCodes = new Regex(@"^\d\d:\d\d:\d\d:\d\d,\d\d:\d\d:\d\d:\d\d,", RegexOptions.Compiled); + foreach (string line in lines) + { + if (line.IndexOf(':') == 2 && regexTimeCodes.IsMatch(line)) + { + string start = line.Substring(0, 11); + string end = line.Substring(12, 11); + + try + { + Paragraph p = new Paragraph(DecodeTimeCode(start), DecodeTimeCode(end), DecodeText(line.Substring(24))); + subtitle.Paragraphs.Add(p); + } + catch + { + _errorCount++; + } + } + else if (!string.IsNullOrWhiteSpace(line) && !line.StartsWith("//") && !line.StartsWith('$')) + { + _errorCount++; + } + } + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string time) + { + //00:01:54:19 + + string hour = time.Substring(0, 2); + string minutes = time.Substring(3, 2); + string seconds = time.Substring(6, 2); + string frames = time.Substring(9, 2); + + int milliseconds = (int)((1000 / 25.0) * int.Parse(frames)); + if (milliseconds > 999) + milliseconds = 999; + + TimeCode tc = new TimeCode(int.Parse(hour), int.Parse(minutes), int.Parse(seconds), milliseconds); + return tc; + } + + private static string DecodeText(string text) + { + text = text.Replace("|", Environment.NewLine); + + //^IBrillstein^I + if (text.Contains(Bold)) + { + text = DecoderTextExtension(text, Bold, ""); + } + if (text.Contains(Italic)) + { + text = DecoderTextExtension(text, Italic, ""); + } + if (text.Contains(Underline)) + { + text = DecoderTextExtension(text, Underline, ""); + } + + return text; + } + + private static string DecoderTextExtension(string text, string SpruceTag, string htmlOpenTag) + { + var htmlCloseTag = htmlOpenTag.Insert(1, "/"); + + var idx = text.IndexOf(SpruceTag, StringComparison.Ordinal); + var c = Utilities.CountTagInText(text, SpruceTag); + if (c == 1) + { + var l = idx + SpruceTag.Length; + if (l < text.Length) + { + text = text.Replace(SpruceTag, htmlOpenTag) + htmlCloseTag; + } + else if (l == text.Length) // Brillstein^I + { + text = text.Remove(text.Length - Italic.Length); + } + } + else if (c > 1) + { + var isOpen = true; + while (idx >= 0) + { + var htmlTag = isOpen ? htmlOpenTag : htmlCloseTag; + text = text.Remove(idx, SpruceTag.Length).Insert(idx, htmlTag); + isOpen = !isOpen; + idx = text.IndexOf(SpruceTag, idx + htmlTag.Length); + } + } + return text; + } + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/SpruceWithSpace.cs b/libse/SubtitleFormats/SpruceWithSpace.cs new file mode 100644 index 000000000..5b8ab6604 --- /dev/null +++ b/libse/SubtitleFormats/SpruceWithSpace.cs @@ -0,0 +1,145 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class SpruceWithSpace : SubtitleFormat + { + + private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d:\d\d:\d\d:\d\d, \d\d:\d\d:\d\d:\d\d,.+", RegexOptions.Compiled); + + public override string Extension + { + get { return ".stl"; } + } + + public override string Name + { + get { return "Spruce Subtitle With Space"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string Header = @"$FontName = Arial +$FontSize = 34 +$HorzAlign = Left +$VertAlign = Bottom +$XOffset = 0 +$YOffset = 0 +$Bold = FALSE +$UnderLined = FALSE +$Italic = FALSE +$TextContrast = 15 +$Outline1Contrast = 15 +$Outline2Contrast = 15 +$BackgroundContrast = 0 +$ForceDisplay = FALSE +$FadeIn = 0 +$FadeOut = 0 +$TapeOffset = FALSE + +\\Colour 0 = Black +\\Colour 1 = Red +\\Colour 2 = Green +\\Colour 3 = Yellow +\\Colour 4 = Blue +\\Colour 5 = Magenta +\\Colour 6 = Cyan +\\Colour 7 = White +"; + StringBuilder sb = new StringBuilder(); + sb.AppendLine(Header); + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.AppendLine(string.Format("$HorzAlign = Center\r\n{0}, {1}, {2}", EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), EncodeText(p.Text))); + } + return sb.ToString(); + } + + private static string EncodeText(string text) + { + text = text.Replace("", "^B"); + text = text.Replace("", string.Empty); + text = text.Replace("", "^I"); + text = text.Replace("", string.Empty); + text = text.Replace("", "^U"); + text = text.Replace("", string.Empty); + return text.Replace(Environment.NewLine, "|"); + } + + private static string EncodeTimeCode(TimeCode time) + { + //00:01:54:19 + return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", time.Hours, time.Minutes, time.Seconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + //00:01:54:19,00:01:56:17,We should be thankful|they accepted our offer. + _errorCount = 0; + subtitle.Paragraphs.Clear(); + foreach (string line in lines) + { + if (line.IndexOf(':') == 2 && RegexTimeCodes.IsMatch(line)) + { + string start = line.Substring(0, 11); + string end = line.Substring(13, 11); + + try + { + Paragraph p = new Paragraph(DecodeTimeCode(start), DecodeTimeCode(end), DecodeText(line.Substring(25).Trim())); + subtitle.Paragraphs.Add(p); + } + catch + { + _errorCount++; + } + } + else if (!string.IsNullOrWhiteSpace(line) && !line.StartsWith("//") && !line.StartsWith('$')) + { + _errorCount++; + } + } + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string time) + { + //00:01:54:19 + + string hour = time.Substring(0, 2); + string minutes = time.Substring(3, 2); + string seconds = time.Substring(6, 2); + string frames = time.Substring(9, 2); + + TimeCode tc = new TimeCode(int.Parse(hour), int.Parse(minutes), int.Parse(seconds), FramesToMillisecondsMax999(int.Parse(frames))); + return tc; + } + + private static string DecodeText(string text) + { // TODO: Improve end tags + text = text.Replace("|", Environment.NewLine); + if (text.Contains("^B")) + text = text.Replace("^B", "") + ""; + if (text.Contains("^I")) + text = text.Replace("^I", "") + ""; + if (text.Contains("^U")) + text = text.Replace("^U", "") + ""; + return text; + } + } +} diff --git a/libse/SubtitleFormats/Spt.cs b/libse/SubtitleFormats/Spt.cs new file mode 100644 index 000000000..6e188ad5a --- /dev/null +++ b/libse/SubtitleFormats/Spt.cs @@ -0,0 +1,181 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class Spt : SubtitleFormat + { + public override string Extension + { + get { return ".spt"; } + } + + public const string NameOfFormat = "spt"; + + public override string Name + { + get { return NameOfFormat; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public static void Save(string fileName, Subtitle subtitle) + { + FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write); + + // header + fs.WriteByte(1); + for (int i = 1; i < 23; i++) + fs.WriteByte(0); + fs.WriteByte(0x60); + + // paragraphs + int number = 0; + foreach (Paragraph p in subtitle.Paragraphs) + { + WriteParagraph(p); + number++; + } + + // footer + fs.WriteByte(0xff); + for (int i = 0; i < 11; i++) + fs.WriteByte(0); + fs.WriteByte(0x11); + byte[] footerBuffer = Encoding.ASCII.GetBytes("dummy end of file"); + fs.Write(footerBuffer, 0, footerBuffer.Length); + + fs.Close(); + } + + private static void WriteParagraph(Paragraph p) + { + WriteTimeCode(); + WriteTimeCode(); + + string text = p.Text; + if (Utilities.GetNumberOfLines(text) > 2) + text = Utilities.AutoBreakLine(p.Text); + + var lines = text.SplitToLines(); + int textLengthFirstLine = 0; + int textLengthSecondLine = 0; + if (lines.Length > 0) + { + textLengthFirstLine = lines[0].Length; + if (lines.Length > 1) + textLengthSecondLine = lines[1].Length; + } + } + + private static void WriteTimeCode() + { + // write 8 bytes time code + } + + public override bool IsMine(List lines, string fileName) + { + if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName)) + { + try + { + FileInfo fi = new FileInfo(fileName); + if (fi.Length > 100 && fi.Length < 1024000) // not too small or too big + { + byte[] buffer = FileUtil.ReadAllBytesShared(fileName); + + if (buffer[00] > 10 && + buffer[01] == 0 && + fileName.EndsWith(".spt", StringComparison.OrdinalIgnoreCase)) + return true; + } + } + catch + { + return false; + } + } + return false; + } + + public override string ToText(Subtitle subtitle, string title) + { + return "Not supported!"; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + subtitle.Paragraphs.Clear(); + byte[] buffer = FileUtil.ReadAllBytesShared(fileName); + + int index = buffer[0]; // go to first subtitle paragraph + while (index < buffer.Length) + { + Paragraph p = GetSptParagraph(ref index, buffer); + if (p != null) + subtitle.Paragraphs.Add(p); + } + subtitle.Renumber(); + } + + private Paragraph GetSptParagraph(ref int index, byte[] buffer) + { + if (index + 16 + 20 + 4 >= buffer.Length) + { + index = index + 16 + 20 + 4; + return null; + } + + int textLengthFirstLine = buffer[index + 16 + 20]; + int textLengthSecondLine = buffer[index + 16 + 20 + 4]; + + if (textLengthFirstLine == 0 && textLengthSecondLine == 0) + { + index += (16 + 20 + 16); + _errorCount++; + return null; + } + + try + { + var p = new Paragraph(); + p.StartTime = GetTimeCode(Encoding.Default.GetString(buffer, index, 8)); + p.EndTime = GetTimeCode(Encoding.Default.GetString(buffer, index + 8, 8)); + + p.Text = Encoding.Default.GetString(buffer, index + 16 + 20 + 16, textLengthFirstLine); + + if (textLengthSecondLine > 0) + p.Text += Environment.NewLine + Encoding.Default.GetString(buffer, index + 16 + 20 + 16 + textLengthFirstLine, textLengthSecondLine); + + index += (16 + 20 + 16 + textLengthFirstLine + textLengthSecondLine); + return p; + } + catch + { + index += (16 + 20 + 16 + textLengthFirstLine + textLengthSecondLine); + _errorCount++; + return null; + } + } + + private static TimeCode GetTimeCode(string timeCode) + { + int hour = int.Parse(timeCode.Substring(0, 2)); + int minute = int.Parse(timeCode.Substring(2, 2)); + int second = int.Parse(timeCode.Substring(4, 2)); + int frames = int.Parse(timeCode.Substring(6, 2)); + + int milliseconds = (int)((1000 / Configuration.Settings.General.CurrentFrameRate) * frames); + if (milliseconds > 999) + milliseconds = 999; + + return new TimeCode(hour, minute, second, milliseconds); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/StructuredTitles.cs b/libse/SubtitleFormats/StructuredTitles.cs new file mode 100644 index 000000000..fe156bb9b --- /dev/null +++ b/libse/SubtitleFormats/StructuredTitles.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class StructuredTitles : SubtitleFormat + { + private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d\d\d : \d\d:\d\d:\d\d:\d\d,\d\d:\d\d:\d\d:\d\d,\d\d", RegexOptions.Compiled); + private static readonly Regex RegexSomeCodes = new Regex(@"^\d\d \d\d \d\d", RegexOptions.Compiled); + private static readonly Regex RegexText = new Regex(@"^[A-Z]\d[A-Z]\d\d ", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Structured titles"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + + var sb = new StringBuilder(); + foreach (string line in lines) + sb.AppendLine(line); + + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + int index = 0; + sb.AppendLine(@"Structured titles +0000 : --:--:--:--,--:--:--:--,10 +80 80 80 +"); + + //0001 : 01:07:25:08,01:07:29:00,10 + //80 80 80 + //C1Y00 Niemand zal je helpen ontsnappen. + //C1Y00 - Een agent heeft me geholpen. + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.AppendLine(string.Format("{0:0000} : {1},{2},10", index + 1, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime))); + sb.AppendLine("80 80 80"); + foreach (string line in p.Text.SplitToLines()) + sb.AppendLine("C1Y00 " + line.Trim()); + sb.AppendLine(); + index++; + } + sb.AppendLine(string.Format("{0:0000}", index + 1) + @" : --:--:--:--,--:--:--:--,-1 +80 80 80"); + return sb.ToString(); + } + + private static string EncodeTimeCode(TimeCode time) + { + //00:03:15:22 (last is frame) + return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", time.Hours, time.Minutes, time.Seconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + //0001 : 01:07:25:08,01:07:29:00,10 + _errorCount = 0; + Paragraph p = null; + subtitle.Paragraphs.Clear(); + foreach (string line in lines) + { + if (line.IndexOf(':') == 5 && RegexTimeCodes.IsMatch(line)) + { + if (p != null) + subtitle.Paragraphs.Add(p); + + string start = line.Substring(7, 11); + string end = line.Substring(19, 11); + + string[] startParts = start.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + string[] endParts = end.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + if (startParts.Length == 4 && endParts.Length == 4) + { + p = new Paragraph(DecodeTimeCode(startParts), DecodeTimeCode(endParts), string.Empty); + } + } + else if (p != null && RegexText.IsMatch(line)) + { + if (string.IsNullOrEmpty(p.Text)) + p.Text = line.Substring(5).Trim(); + else + p.Text += Environment.NewLine + line.Substring(5).Trim(); + } + else if (line.Length < 10 && RegexSomeCodes.IsMatch(line)) + { + } + else if (string.IsNullOrWhiteSpace(line)) + { + // skip these lines + } + else if (p != null) + { + if (p.Text != null && Utilities.GetNumberOfLines(p.Text) > 3) + { + _errorCount++; + } + else + { + if (!line.TrimEnd().EndsWith(": --:--:--:--,--:--:--:--,-1", StringComparison.Ordinal)) + { + if (string.IsNullOrEmpty(p.Text)) + p.Text = line.Trim(); + else + p.Text += Environment.NewLine + line.Trim(); + } + } + } + } + if (p != null && !string.IsNullOrEmpty(p.Text)) + subtitle.Paragraphs.Add(p); + + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + //00:00:07:12 + var hour = int.Parse(parts[0]); + var minutes = int.Parse(parts[1]); + var seconds = int.Parse(parts[2]); + var frames = int.Parse(parts[3]); + + return new TimeCode(hour, minutes, seconds, FramesToMillisecondsMax999(frames)); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/SubRip.cs b/libse/SubtitleFormats/SubRip.cs new file mode 100644 index 000000000..fedd6b876 --- /dev/null +++ b/libse/SubtitleFormats/SubRip.cs @@ -0,0 +1,278 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class SubRip : SubtitleFormat + { + public string Errors { get; private set; } + private StringBuilder _errors; + private int _lineNumber; + + private enum ExpectingLine + { + Number, + TimeCodes, + Text + } + + private Paragraph _paragraph; + private Paragraph _lastParagraph; + private ExpectingLine _expecting = ExpectingLine.Number; + private static readonly Regex RegexTimeCodes = new Regex(@"^-?\d+:-?\d+:-?\d+[:,]-?\d+\s*-->\s*-?\d+:-?\d+:-?\d+[:,]-?\d+$", RegexOptions.Compiled); + private static readonly Regex RegexTimeCodes2 = new Regex(@"^\d+:\d+:\d+,\d+\s*-->\s*\d+:\d+:\d+,\d+$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".srt"; } + } + + public const string NameOfFormat = "SubRip"; + + public override string Name + { + get { return NameOfFormat; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + if (lines.Count > 0 && lines[0].StartsWith("WEBVTT", StringComparison.OrdinalIgnoreCase)) + return false; + + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + Errors = null; + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string paragraphWriteFormat = "{0}\r\n{1} --> {2}\r\n{3}\r\n\r\n"; + + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + //string s = p.Text.Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine).Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine); + sb.AppendFormat(paragraphWriteFormat, p.Number, p.StartTime, p.EndTime, p.Text); + } + return sb.ToString().Trim() + Environment.NewLine + Environment.NewLine; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + bool doRenum = false; + _errors = new StringBuilder(); + _lineNumber = 0; + + _paragraph = new Paragraph(); + _expecting = ExpectingLine.Number; + _errorCount = 0; + + subtitle.Paragraphs.Clear(); + for (int i = 0; i < lines.Count; i++) + { + _lineNumber++; + string line = lines[i].TrimEnd(); + line = line.Trim('\u007F'); // 127=delete acscii + + string next = string.Empty; + if (i + 1 < lines.Count) + next = lines[i + 1]; + + string nextNext = string.Empty; + if (i + 2 < lines.Count) + nextNext = lines[i + 2]; + + // A new line is missing between two paragraphs (buggy srt file) + if (_expecting == ExpectingLine.Text && i + 1 < lines.Count && + _paragraph != null && !string.IsNullOrEmpty(_paragraph.Text) && Utilities.IsInteger(line) && + RegexTimeCodes.IsMatch(lines[i + 1])) + { + ReadLine(subtitle, string.Empty, string.Empty, string.Empty); + } + if (_expecting == ExpectingLine.Number && RegexTimeCodes.IsMatch(line)) + { + _expecting = ExpectingLine.TimeCodes; + doRenum = true; + } + + ReadLine(subtitle, line, next, nextNext); + } + if (_paragraph != null && _paragraph.EndTime.TotalMilliseconds > _paragraph.StartTime.TotalMilliseconds) + subtitle.Paragraphs.Add(_paragraph); + + //foreach (Paragraph p in subtitle.Paragraphs) + // p.Text = p.Text.Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine); + + if (doRenum) + subtitle.Renumber(); + + Errors = _errors.ToString(); + } + + private void ReadLine(Subtitle subtitle, string line, string next, string nextNext) + { + switch (_expecting) + { + case ExpectingLine.Number: + int number; + if (int.TryParse(line, out number)) + { + _paragraph.Number = number; + _expecting = ExpectingLine.TimeCodes; + } + else if (!string.IsNullOrWhiteSpace(line)) + { + if (_lastParagraph != null && nextNext != null && (_lastParagraph.Number + 1).ToString(CultureInfo.InvariantCulture) == nextNext) + { + _lastParagraph.Text = (_lastParagraph.Text + Environment.NewLine + line.Trim()).Trim(); + } + else + { + if (_errors.Length < 2000) + _errors.AppendLine(string.Format(Configuration.Settings.Language.Main.LineNumberXExpectedNumberFromSourceLineY, _lineNumber, line)); + _errorCount++; + } + } + break; + case ExpectingLine.TimeCodes: + if (TryReadTimeCodesLine(line, _paragraph)) + { + _paragraph.Text = string.Empty; + _expecting = ExpectingLine.Text; + } + else if (!string.IsNullOrWhiteSpace(line)) + { + if (_errors.Length < 2000) + _errors.AppendLine(string.Format(Configuration.Settings.Language.Main.LineNumberXErrorReadingTimeCodeFromSourceLineY, _lineNumber, line)); + _errorCount++; + _expecting = ExpectingLine.Number; // lets go to next paragraph + } + break; + case ExpectingLine.Text: + if (!string.IsNullOrWhiteSpace(line)) + { + if (_paragraph.Text.Length > 0) + _paragraph.Text += Environment.NewLine; + _paragraph.Text += RemoveBadChars(line).TrimEnd().Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine); + } + else if (IsText(next)) + { + if (_paragraph.Text.Length > 0) + _paragraph.Text += Environment.NewLine; + _paragraph.Text += RemoveBadChars(line).TrimEnd().Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine); + } + else if (string.IsNullOrEmpty(line) && string.IsNullOrEmpty(_paragraph.Text)) + { + _paragraph.Text = string.Empty; + if (!string.IsNullOrEmpty(next) && (Utilities.IsInteger(next) || RegexTimeCodes.IsMatch(next))) + { + subtitle.Paragraphs.Add(_paragraph); + _lastParagraph = _paragraph; + _paragraph = new Paragraph(); + _expecting = ExpectingLine.Number; + } + } + else + { + subtitle.Paragraphs.Add(_paragraph); + _lastParagraph = _paragraph; + _paragraph = new Paragraph(); + _expecting = ExpectingLine.Number; + } + break; + } + } + + private static bool IsText(string text) + { + if (string.IsNullOrWhiteSpace(text) || Utilities.IsInteger(text) || RegexTimeCodes.IsMatch(text)) + return false; + return true; + } + + private static string RemoveBadChars(string line) + { + return line.Replace('\0', ' '); + } + + private static bool TryReadTimeCodesLine(string line, Paragraph paragraph) + { + line = line.Replace('،', ','); + line = line.Replace('', ','); + line = line.Replace('¡', ','); + + const string defaultSeparator = " --> "; + // Fix some badly formatted separator sequences - anything can happen if you manually edit ;) + line = line.Replace(" -> ", defaultSeparator); // I've seen this + line = line.Replace(" - > ", defaultSeparator); + line = line.Replace(" ->> ", defaultSeparator); + line = line.Replace(" -- > ", defaultSeparator); + line = line.Replace(" - -> ", defaultSeparator); + line = line.Replace(" -->> ", defaultSeparator); + line = line.Replace(" ---> ", defaultSeparator); + + // Removed stuff after timecodes - like subtitle position + // - example of position info: 00:02:26,407 --> 00:02:31,356 X1:100 X2:100 Y1:100 Y2:100 + if (line.Length > 30 && line[29] == ' ') + line = line.Substring(0, 29); + + // removes all extra spaces + line = line.Replace(" ", string.Empty).Replace("-->", defaultSeparator).Trim(); + + // Fix a few more cases of wrong time codes, seen this: 00.00.02,000 --> 00.00.04,000 + line = line.Replace('.', ':'); + if (line.Length >= 29 && (line[8] == ':' || line[8] == ';')) + line = line.Substring(0, 8) + ',' + line.Substring(8 + 1); + if (line.Length >= 29 && line.Length <= 30 && (line[25] == ':' || line[25] == ';')) + line = line.Substring(0, 25) + ',' + line.Substring(25 + 1); + + if (RegexTimeCodes.IsMatch(line) || RegexTimeCodes2.IsMatch(line)) + { + string[] parts = line.Replace("-->", ":").Replace(" ", string.Empty).Split(':', ','); + try + { + int startHours = int.Parse(parts[0]); + int startMinutes = int.Parse(parts[1]); + int startSeconds = int.Parse(parts[2]); + int startMilliseconds = int.Parse(parts[3]); + int endHours = int.Parse(parts[4]); + int endMinutes = int.Parse(parts[5]); + int endSeconds = int.Parse(parts[6]); + int endMilliseconds = int.Parse(parts[7]); + + paragraph.StartTime = new TimeCode(startHours, startMinutes, startSeconds, startMilliseconds); + if (parts[0].StartsWith('-') && paragraph.StartTime.TotalMilliseconds > 0) + paragraph.StartTime.TotalMilliseconds = paragraph.StartTime.TotalMilliseconds * -1; + + paragraph.EndTime = new TimeCode(endHours, endMinutes, endSeconds, endMilliseconds); + if (parts[4].StartsWith('-') && paragraph.EndTime.TotalMilliseconds > 0) + paragraph.EndTime.TotalMilliseconds = paragraph.EndTime.TotalMilliseconds * -1; + + return true; + } + catch + { + return false; + } + } + return false; + } + + public override List AlternateExtensions + { + get + { + return new List { ".wsrt" }; + } + } + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/SubStationAlpha.cs b/libse/SubtitleFormats/SubStationAlpha.cs new file mode 100644 index 000000000..02463448a --- /dev/null +++ b/libse/SubtitleFormats/SubStationAlpha.cs @@ -0,0 +1,525 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class SubStationAlpha : SubtitleFormat + { + + public string Errors { get; private set; } + + public override string Extension + { + get { return ".ssa"; } + } + + public const string NameOfFormat = "Sub Station Alpha"; + + public override string Name + { + get { return NameOfFormat; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + Errors = null; + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string header = +@"[Script Info] +; This is a Sub Station Alpha v4 script. +Title: {0} +ScriptType: v4.00 +Collisions: Normal +PlayDepth: 0 + +[V4 Styles] +Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding +Style: Default,{1},{2},{3},65535,65535,-2147483640,-1,0,1,{4},{5},2,10,10,10,0,1 + +[Events] +Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text"; + + const string headerNoStyles = +@"[Script Info] +; This is a Sub Station Alpha v4 script. +Title: {0} +ScriptType: v4.00 +Collisions: Normal +PlayDepth: 0 + +[V4 Styles] +Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding +{1} + +[Events] +Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text"; + + const string timeCodeFormat = "{0}:{1:00}:{2:00}.{3:00}"; // h:mm:ss.cc + const string paragraphWriteFormat = "Dialogue: Marked={4},{0},{1},{3},{5},0000,0000,0000,{6},{2}"; + const string commentWriteFormat = "Comment: Marked={4},{0},{1},{3},{5},0000,0000,0000,{6},{2}"; + + var sb = new StringBuilder(); + Color fontColor = Color.FromArgb(Configuration.Settings.SubtitleSettings.SsaFontColorArgb); + bool isValidAssHeader = !string.IsNullOrEmpty(subtitle.Header) && subtitle.Header.Contains("[V4 Styles]"); + List styles = new List(); + if (isValidAssHeader) + { + sb.AppendLine(subtitle.Header.Trim()); + const string formatLine = "Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text"; + if (!subtitle.Header.Contains(formatLine)) + sb.AppendLine(formatLine); + styles = AdvancedSubStationAlpha.GetStylesFromHeader(subtitle.Header); + } + else if (!string.IsNullOrEmpty(subtitle.Header) && subtitle.Header.Contains("[V4+ Styles]")) + { + LoadStylesFromAdvancedSubstationAlpha(subtitle, title, subtitle.Header, headerNoStyles, sb); + } + else if (subtitle.Header != null && subtitle.Header.Contains("http://www.w3.org/ns/ttml")) + { + LoadStylesFromTimedText10(subtitle, title, header, headerNoStyles, sb); + } + else + { + sb.AppendLine(string.Format(header, + title, + Configuration.Settings.SubtitleSettings.SsaFontName, + (int)Configuration.Settings.SubtitleSettings.SsaFontSize, + ColorTranslator.ToWin32(fontColor), + Configuration.Settings.SubtitleSettings.SsaOutline, + Configuration.Settings.SubtitleSettings.SsaShadow + )); + } + foreach (Paragraph p in subtitle.Paragraphs) + { + string start = string.Format(timeCodeFormat, p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, p.StartTime.Milliseconds / 10); + string end = string.Format(timeCodeFormat, p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, p.EndTime.Milliseconds / 10); + string style = "Default"; + string actor = "NTP"; + if (!string.IsNullOrEmpty(p.Actor)) + actor = p.Actor; + string effect = ""; + if (!string.IsNullOrEmpty(p.Effect)) + effect = p.Effect; + if (!string.IsNullOrEmpty(p.Extra) && isValidAssHeader && styles.Contains(p.Extra)) + style = p.Extra; + if (style == "Default") + style = "*Default"; + if (p.IsComment) + sb.AppendLine(string.Format(commentWriteFormat, start, end, AdvancedSubStationAlpha.FormatText(p), style, p.Layer, actor, effect)); + else + sb.AppendLine(string.Format(paragraphWriteFormat, start, end, AdvancedSubStationAlpha.FormatText(p), style, p.Layer, actor, effect)); + } + return sb.ToString().Trim(); + } + + private static void LoadStylesFromAdvancedSubstationAlpha(Subtitle subtitle, string title, string header, string headerNoStyles, StringBuilder sb) + { + try + { + bool styleFound = false; + var ttStyles = new StringBuilder(); + foreach (string styleName in AdvancedSubStationAlpha.GetStylesFromHeader(subtitle.Header)) + { + try + { + var ssaStyle = AdvancedSubStationAlpha.GetSsaStyle(styleName, subtitle.Header); + if (ssaStyle != null) + { + string bold = "-1"; + if (ssaStyle.Bold) + bold = "1"; + string italic = "0"; + if (ssaStyle.Italic) + italic = "1"; + + string newAlignment = "2"; + switch (ssaStyle.Alignment) + { + case "1": + newAlignment = "1"; + break; + case "3": + newAlignment = "3"; + break; + case "4": + newAlignment = "9"; + break; + case "5": + newAlignment = "10"; + break; + case "6": + newAlignment = "11"; + break; + case "7": + newAlignment = "5"; + break; + case "8": + newAlignment = "6"; + break; + case "9": + newAlignment = "7"; + break; + } + + //Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding + const string styleFormat = "Style: {0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},0,1"; + // N FN FS PC SC TC BC Bo It BS O Sh Ali ML MR MV A Encoding + + ttStyles.AppendLine(string.Format(styleFormat, ssaStyle.Name, ssaStyle.FontName, ssaStyle.FontSize, ssaStyle.Primary.ToArgb(), ssaStyle.Secondary.ToArgb(), + ssaStyle.Outline.ToArgb(), ssaStyle.Background.ToArgb(), bold, italic, ssaStyle.BorderStyle, ssaStyle.OutlineWidth, ssaStyle.ShadowWidth, + newAlignment, ssaStyle.MarginLeft, ssaStyle.MarginRight, ssaStyle.MarginVertical)); + styleFound = true; + } + } + catch + { + } + } + + if (styleFound) + { + sb.AppendLine(string.Format(headerNoStyles, title, ttStyles)); + subtitle.Header = sb.ToString(); + } + else + { + sb.AppendLine(string.Format(header, title)); + } + } + catch + { + sb.AppendLine(string.Format(header, title)); + } + } + + private static void LoadStylesFromTimedText10(Subtitle subtitle, string title, string header, string headerNoStyles, StringBuilder sb) + { + try + { + var lines = new List(); + foreach (string s in subtitle.Header.Replace(Environment.NewLine, "\n").Split('\n')) + lines.Add(s); + var tt = new TimedText10(); + var sub = new Subtitle(); + tt.LoadSubtitle(sub, lines, string.Empty); + + var xml = new XmlDocument(); + xml.LoadXml(subtitle.Header); + var nsmgr = new XmlNamespaceManager(xml.NameTable); + nsmgr.AddNamespace("ttml", "http://www.w3.org/ns/ttml"); + XmlNode head = xml.DocumentElement.SelectSingleNode("ttml:head", nsmgr); + int stylexmlCount = 0; + var ttStyles = new StringBuilder(); + foreach (XmlNode node in head.SelectNodes("//ttml:style", nsmgr)) + { + string name = null; + if (node.Attributes["xml:id"] != null) + name = node.Attributes["xml:id"].Value; + else if (node.Attributes["id"] != null) + name = node.Attributes["id"].Value; + if (name != null) + { + stylexmlCount++; + + string fontFamily = "Arial"; + if (node.Attributes["tts:fontFamily"] != null) + fontFamily = node.Attributes["tts:fontFamily"].Value; + + string fontWeight = "normal"; + if (node.Attributes["tts:fontWeight"] != null) + fontWeight = node.Attributes["tts:fontWeight"].Value; + + string fontStyle = "normal"; + if (node.Attributes["tts:fontStyle"] != null) + fontStyle = node.Attributes["tts:fontStyle"].Value; + + string color = "#ffffff"; + if (node.Attributes["tts:color"] != null) + color = node.Attributes["tts:color"].Value.Trim(); + Color c = Color.White; + try + { + if (color.StartsWith("rgb(", StringComparison.Ordinal)) + { + string[] arr = color.Remove(0, 4).TrimEnd(')').Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + c = Color.FromArgb(int.Parse(arr[0]), int.Parse(arr[1]), int.Parse(arr[2])); + } + else + { + c = ColorTranslator.FromHtml(color); + } + } + catch + { + } + + string fontSize = "20"; + if (node.Attributes["tts:fontSize"] != null) + fontSize = node.Attributes["tts:fontSize"].Value.Replace("px", string.Empty).Replace("em", string.Empty); + int fSize; + if (!int.TryParse(fontSize, out fSize)) + fSize = 20; + + const string styleFormat = "Style: {0},{1},{2},{3},65535,65535,-2147483640,-1,0,1,3,0,2,10,10,10,0,1"; + + ttStyles.AppendLine(string.Format(styleFormat, name, fontFamily, fSize, c.ToArgb())); + } + } + + if (stylexmlCount > 0) + { + sb.AppendLine(string.Format(headerNoStyles, title, ttStyles)); + subtitle.Header = sb.ToString(); + } + else + { + sb.AppendLine(string.Format(header, title)); + } + } + catch + { + sb.AppendLine(string.Format(header, title)); + } + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + Errors = null; + bool eventsStarted = false; + subtitle.Paragraphs.Clear(); + string[] format = { "Marked", " Start", " End", " Style", " Name", " MarginL", " MarginR", " MarginV", " Effect", " Text" }; + int indexLayer = 0; + int indexStart = 1; + int indexEnd = 2; + int indexStyle = 3; + const int indexName = 4; + int indexEffect = 8; + int indexText = 9; + var errors = new StringBuilder(); + int lineNumber = 0; + + var header = new StringBuilder(); + foreach (string line in lines) + { + lineNumber++; + if (!eventsStarted) + header.AppendLine(line); + + if (line.Trim().Equals("[events]", StringComparison.OrdinalIgnoreCase)) + { + eventsStarted = true; + } + else if (!string.IsNullOrEmpty(line) && line.TrimStart().StartsWith(';')) + { + // skip comment lines + } + else if (eventsStarted && !string.IsNullOrWhiteSpace(line)) + { + string s = line.Trim().ToLower(); + if (s.StartsWith("format:", StringComparison.Ordinal)) + { + if (line.Length > 10) + { + format = line.ToLower().Substring(8).Split(','); + for (int i = 0; i < format.Length; i++) + { + if (format[i].Trim().Equals("layer", StringComparison.OrdinalIgnoreCase)) + indexLayer = i; + else if (format[i].Trim().Equals("start", StringComparison.OrdinalIgnoreCase)) + indexStart = i; + else if (format[i].Trim().Equals("end", StringComparison.OrdinalIgnoreCase)) + indexEnd = i; + else if (format[i].Trim().Equals("text", StringComparison.OrdinalIgnoreCase)) + indexText = i; + else if (format[i].Trim().Equals("effect", StringComparison.OrdinalIgnoreCase)) + indexEffect = i; + else if (format[i].Trim().Equals("style", StringComparison.OrdinalIgnoreCase)) + indexStyle = i; + } + } + } + else if (!string.IsNullOrEmpty(s)) + { + string text = string.Empty; + string start = string.Empty; + string end = string.Empty; + string style = string.Empty; + var layer = 0; + string effect = string.Empty; + string name = string.Empty; + + string[] splittedLine; + if (s.StartsWith("dialog:", StringComparison.Ordinal)) + splittedLine = line.Remove(0, 7).Split(','); + else if (s.StartsWith("dialogue:", StringComparison.Ordinal)) + splittedLine = line.Remove(0, 9).Split(','); + else + splittedLine = line.Split(','); + + for (int i = 0; i < splittedLine.Length; i++) + { + if (i == indexStart) + start = splittedLine[i].Trim(); + else if (i == indexEnd) + end = splittedLine[i].Trim(); + else if (i == indexLayer) + int.TryParse(splittedLine[i], out layer); + else if (i == indexEffect) + effect = splittedLine[i]; + else if (i == indexText) + text = splittedLine[i]; + else if (i == indexStyle) + style = splittedLine[i]; + else if (i == indexName) + name = splittedLine[i]; + else if (i > indexText) + text += "," + splittedLine[i]; + } + + try + { + var p = new Paragraph + { + StartTime = GetTimeCodeFromString(start), + EndTime = GetTimeCodeFromString(end), + Text = AdvancedSubStationAlpha.GetFormattedText(text) + }; + + if (!string.IsNullOrEmpty(style)) + p.Extra = style; + if (!string.IsNullOrEmpty(effect)) + p.Effect = effect; + p.Layer = layer; + if (!string.IsNullOrEmpty(name)) + p.Actor = name; + p.IsComment = s.StartsWith("comment:", StringComparison.Ordinal); + subtitle.Paragraphs.Add(p); + } + catch + { + _errorCount++; + if (errors.Length < 2000) + errors.AppendLine(string.Format(Configuration.Settings.Language.Main.LineNumberXErrorReadingTimeCodeFromSourceLineY, lineNumber, line)); + } + } + } + } + if (header.Length > 0) + subtitle.Header = header.ToString(); + subtitle.Renumber(); + Errors = errors.ToString(); + } + + private static TimeCode GetTimeCodeFromString(string time) + { + // h:mm:ss.cc + string[] timeCode = time.Split(':', '.'); + return new TimeCode(int.Parse(timeCode[0]), + int.Parse(timeCode[1]), + int.Parse(timeCode[2]), + int.Parse(timeCode[3]) * 10); + } + + public override void RemoveNativeFormatting(Subtitle subtitle, SubtitleFormat newFormat) + { + if (newFormat != null && newFormat.Name == AdvancedSubStationAlpha.NameOfFormat) + { + // do we need any conversion? + } + else + { + foreach (Paragraph p in subtitle.Paragraphs) + { + int indexOfBegin = p.Text.IndexOf('{'); + string pre = string.Empty; + while (indexOfBegin >= 0 && p.Text.IndexOf('}') > indexOfBegin) + { + string s = p.Text.Substring(indexOfBegin); + if (s.StartsWith("{\\an1}", StringComparison.Ordinal) || + s.StartsWith("{\\an2}", StringComparison.Ordinal) || + s.StartsWith("{\\an3}", StringComparison.Ordinal) || + s.StartsWith("{\\an4}", StringComparison.Ordinal) || + s.StartsWith("{\\an5}", StringComparison.Ordinal) || + s.StartsWith("{\\an6}", StringComparison.Ordinal) || + s.StartsWith("{\\an7}", StringComparison.Ordinal) || + s.StartsWith("{\\an8}", StringComparison.Ordinal) || + s.StartsWith("{\\an9}", StringComparison.Ordinal)) + { + pre = s.Substring(0, 6); + } + else if (s.StartsWith("{\\an1\\", StringComparison.Ordinal) || + s.StartsWith("{\\an2\\", StringComparison.Ordinal) || + s.StartsWith("{\\an3\\", StringComparison.Ordinal) || + s.StartsWith("{\\an4\\", StringComparison.Ordinal) || + s.StartsWith("{\\an5\\", StringComparison.Ordinal) || + s.StartsWith("{\\an6\\", StringComparison.Ordinal) || + s.StartsWith("{\\an7\\", StringComparison.Ordinal) || + s.StartsWith("{\\an8\\", StringComparison.Ordinal) || + s.StartsWith("{\\an9\\", StringComparison.Ordinal)) + { + pre = s.Substring(0, 5) + "}"; + } + else if (s.StartsWith("{\\a1}", StringComparison.Ordinal) || s.StartsWith("{\\a1\\", StringComparison.Ordinal) || + s.StartsWith("{\\a3}", StringComparison.Ordinal) || s.StartsWith("{\\a3\\", StringComparison.Ordinal)) + { + pre = s.Substring(0, 4) + "}"; + } + else if (s.StartsWith("{\\a9}", StringComparison.Ordinal) || s.StartsWith("{\\a9\\", StringComparison.Ordinal)) + { + pre = "{\\an4}"; + } + else if (s.StartsWith("{\\a10}", StringComparison.Ordinal) || s.StartsWith("{\\a10\\", StringComparison.Ordinal)) + { + pre = "{\\an5}"; + } + else if (s.StartsWith("{\\a11}", StringComparison.Ordinal) || s.StartsWith("{\\a11\\", StringComparison.Ordinal)) + { + pre = "{\\an6}"; + } + else if (s.StartsWith("{\\a5}", StringComparison.Ordinal) || s.StartsWith("{\\a5\\", StringComparison.Ordinal)) + { + pre = "{\\an7}"; + } + else if (s.StartsWith("{\\a6}", StringComparison.Ordinal) || s.StartsWith("{\\a6\\", StringComparison.Ordinal)) + { + pre = "{\\an8}"; + } + else if (s.StartsWith("{\\a7}", StringComparison.Ordinal) || s.StartsWith("{\\a7\\", StringComparison.Ordinal)) + { + pre = "{\\an9}"; + } + int indexOfEnd = p.Text.IndexOf('}'); + p.Text = p.Text.Remove(indexOfBegin, (indexOfEnd - indexOfBegin) + 1); + + indexOfBegin = p.Text.IndexOf('{'); + } + p.Text = pre + p.Text; + } + } + } + + public override bool HasStyleSupport + { + get + { + return true; + } + } + + } +} diff --git a/libse/SubtitleFormats/SubViewer10.cs b/libse/SubtitleFormats/SubViewer10.cs new file mode 100644 index 000000000..dbab2c7a7 --- /dev/null +++ b/libse/SubtitleFormats/SubViewer10.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class SubViewer10 : SubtitleFormat + { + private static readonly Regex RegexTimeCode = new Regex(@"^\[\d\d:\d\d:\d\d\]$", RegexOptions.Compiled); + + private enum ExpectingLine + { + TimeStart, + Text, + TimeEnd, + } + + public override string Extension + { + get { return ".sub"; } + } + + public override string Name + { + get { return "SubViewer 1.0"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + //[00:02:14] + //Yes a new line|Line number 2 + //[00:02:15] + string paragraphWriteFormat = "[{0:00}:{1:00}:{2:00}]" + Environment.NewLine + + "{3}" + Environment.NewLine + + "[{4:00}:{5:00}:{6:00}]"; + const string header = @"[TITLE] +{0} +[AUTHOR] +[SOURCE] +[PRG] +[FILEPATH] +[DELAY] +0 +[CD TRACK] +0 +[BEGIN] +******** START SCRIPT ******** +"; + const string footer = @"[end] +******** END SCRIPT ******** +"; + var sb = new StringBuilder(); + sb.AppendFormat(header, title); + foreach (Paragraph p in subtitle.Paragraphs) + { + string text = HtmlUtil.RemoveHtmlTags(p.Text.Replace(Environment.NewLine, "|")); + + sb.AppendLine(string.Format(paragraphWriteFormat, + p.StartTime.Hours, + p.StartTime.Minutes, + p.StartTime.Seconds, + text, + p.EndTime.Hours, + p.EndTime.Minutes, + p.EndTime.Seconds)); + sb.AppendLine(); + } + sb.Append(footer); + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + var paragraph = new Paragraph(); + ExpectingLine expecting = ExpectingLine.TimeStart; + _errorCount = 0; + + subtitle.Paragraphs.Clear(); + foreach (string line in lines) + { + if (line.StartsWith('[') && RegexTimeCode.IsMatch(line)) + { + string[] parts = line.Split(new[] { ':', ']', '[', ' ' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 3) + { + try + { + int startHours = int.Parse(parts[0]); + int startMinutes = int.Parse(parts[1]); + int startSeconds = int.Parse(parts[2]); + var tc = new TimeCode(startHours, startMinutes, startSeconds, 0); + if (expecting == ExpectingLine.TimeStart) + { + paragraph = new Paragraph(); + paragraph.StartTime = tc; + expecting = ExpectingLine.Text; + } + else if (expecting == ExpectingLine.TimeEnd) + { + paragraph.EndTime = tc; + expecting = ExpectingLine.TimeStart; + subtitle.Paragraphs.Add(paragraph); + paragraph = new Paragraph(); + } + } + catch + { + _errorCount++; + expecting = ExpectingLine.TimeStart; + } + } + } + else + { + if (expecting == ExpectingLine.Text) + { + if (line.Length > 0) + { + string text = line.Replace("|", Environment.NewLine); + paragraph.Text = text; + expecting = ExpectingLine.TimeEnd; + } + } + } + } + subtitle.Renumber(); + } + } +} diff --git a/libse/SubtitleFormats/SubViewer20.cs b/libse/SubtitleFormats/SubViewer20.cs new file mode 100644 index 000000000..58f5ddf8f --- /dev/null +++ b/libse/SubtitleFormats/SubViewer20.cs @@ -0,0 +1,161 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class SubViewer20 : SubtitleFormat + { + private enum ExpectingLine + { + TimeCodes, + Text + } + + public override string Extension + { + get { return ".sub"; } + } + + public override string Name + { + get { return "SubViewer 2.0"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var sbv = new YouTubeSbv(); + if (sbv.IsMine(lines, fileName) && !String.Join(String.Empty, lines.ToArray()).Contains("[br]")) + return false; + + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string paragraphWriteFormat = "{0:00}:{1:00}:{2:00}.{3:00},{4:00}:{5:00}:{6:00}.{7:00}{8}{9}"; + const string header = @"[INFORMATION] +[TITLE]{0} +[AUTHOR] +[SOURCE] +[PRG] +[FILEPATH] +[DELAY]0 +[CD TRACK]0 +[COMMENT] +[END INFORMATION] +[SUBTITLE] +[COLF]&H000000,[STYLE]bd,[SIZE]25,[FONT]Arial +"; + //00:00:06.61,00:00:13.75 + //text1[br]text2 + var sb = new StringBuilder(); + sb.AppendFormat(header, title); + foreach (Paragraph p in subtitle.Paragraphs) + { + string text = p.Text.Replace(Environment.NewLine, "[br]"); + text = text.Replace("", "{\\i1}"); + text = text.Replace("", "{\\i0}"); + text = text.Replace("", "{\\i}"); + text = text.Replace("", "{\\b1}'"); + text = text.Replace("", "{\\b0}"); + text = text.Replace("", "{\\b}"); + text = text.Replace("", "{\\u1}"); + text = text.Replace("", "{\\u0}"); + text = text.Replace("", "{\\u}"); + + sb.AppendLine(string.Format(paragraphWriteFormat, + p.StartTime.Hours, + p.StartTime.Minutes, + p.StartTime.Seconds, + RoundTo2Cifres(p.StartTime.Milliseconds), + p.EndTime.Hours, + p.EndTime.Minutes, + p.EndTime.Seconds, + RoundTo2Cifres(p.EndTime.Milliseconds), + Environment.NewLine, + text)); + sb.AppendLine(); + } + return sb.ToString().Trim(); + } + + private static int RoundTo2Cifres(int milliseconds) + { + int rounded = (int)Math.Round((double)milliseconds / 10); + return rounded; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + var regexTimeCodes = new Regex(@"^\d\d:\d\d:\d\d.\d+,\d\d:\d\d:\d\d.\d+$", RegexOptions.Compiled); + + var paragraph = new Paragraph(); + ExpectingLine expecting = ExpectingLine.TimeCodes; + _errorCount = 0; + + subtitle.Paragraphs.Clear(); + foreach (string line in lines) + { + if (regexTimeCodes.IsMatch(line)) + { + string[] parts = line.Split(new[] { ':', ',', '.' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 8) + { + try + { + int startHours = int.Parse(parts[0]); + int startMinutes = int.Parse(parts[1]); + int startSeconds = int.Parse(parts[2]); + int startMilliseconds = int.Parse(parts[3]); + int endHours = int.Parse(parts[4]); + int endMinutes = int.Parse(parts[5]); + int endSeconds = int.Parse(parts[6]); + int endMilliseconds = int.Parse(parts[7]); + paragraph.StartTime = new TimeCode(startHours, startMinutes, startSeconds, startMilliseconds); + paragraph.EndTime = new TimeCode(endHours, endMinutes, endSeconds, endMilliseconds); + expecting = ExpectingLine.Text; + } + catch + { + expecting = ExpectingLine.TimeCodes; + } + } + } + else + { + if (expecting == ExpectingLine.Text) + { + if (line.Length > 0) + { + string text = line.Replace("[br]", Environment.NewLine); + text = text.Replace("{\\i1}", ""); + text = text.Replace("{\\i0}", ""); + text = text.Replace("{\\i}", ""); + text = text.Replace("{\\b1}", "'"); + text = text.Replace("{\\b0}", ""); + text = text.Replace("{\\b}", ""); + text = text.Replace("{\\u1}", ""); + text = text.Replace("{\\u0}", ""); + text = text.Replace("{\\u}", ""); + + paragraph.Text = text; + subtitle.Paragraphs.Add(paragraph); + paragraph = new Paragraph(); + expecting = ExpectingLine.TimeCodes; + } + } + } + } + subtitle.Renumber(); + } + } +} diff --git a/libse/SubtitleFormats/SubtitleEditorProject.cs b/libse/SubtitleFormats/SubtitleEditorProject.cs new file mode 100644 index 000000000..c1233f410 --- /dev/null +++ b/libse/SubtitleFormats/SubtitleEditorProject.cs @@ -0,0 +1,168 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class SubtitleEditorProject : SubtitleFormat + { + public override string Extension + { + get { return ".xml"; } + } + + public override string Name + { + get { return "Subtitle Editor Project"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + string xmlAsString = sb.ToString().Trim(); + if (xmlAsString.Contains(" 0; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + return false; + } + } + return false; + } + + public override string ToText(Subtitle subtitle, string title) + { + string xmlStructure = + "" + Environment.NewLine + + "" + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + ""; + + var xml = new XmlDocument(); + xml.LoadXml(xmlStructure); + + // + XmlNode div = xml.DocumentElement.SelectSingleNode("subtitles"); + foreach (Paragraph p in subtitle.Paragraphs) + { + XmlNode paragraph = xml.CreateElement("subtitle"); + + XmlAttribute duration = xml.CreateAttribute("duration"); + duration.InnerText = ((int)Math.Round(p.Duration.TotalMilliseconds)).ToString(CultureInfo.InvariantCulture); + paragraph.Attributes.Append(duration); + + XmlAttribute effect = xml.CreateAttribute("effect"); + effect.InnerText = string.Empty; + paragraph.Attributes.Append(effect); + + XmlAttribute end = xml.CreateAttribute("end"); + end.InnerText = ((int)Math.Round(p.EndTime.TotalMilliseconds)).ToString(CultureInfo.InvariantCulture); + paragraph.Attributes.Append(end); + + XmlAttribute layer = xml.CreateAttribute("layer"); + layer.InnerText = "0"; + paragraph.Attributes.Append(layer); + + XmlAttribute marginL = xml.CreateAttribute("margin-l"); + marginL.InnerText = "0"; + paragraph.Attributes.Append(marginL); + + XmlAttribute marginR = xml.CreateAttribute("margin-r"); + marginR.InnerText = "0"; + paragraph.Attributes.Append(marginR); + + XmlAttribute marginV = xml.CreateAttribute("margin-v"); + marginV.InnerText = "0"; + paragraph.Attributes.Append(marginV); + + XmlAttribute name = xml.CreateAttribute("name"); + name.InnerText = string.Empty; + paragraph.Attributes.Append(name); + + XmlAttribute note = xml.CreateAttribute("note"); + note.InnerText = string.Empty; + paragraph.Attributes.Append(note); + + XmlAttribute path = xml.CreateAttribute("path"); + path.InnerText = "0"; + paragraph.Attributes.Append(path); + + XmlAttribute start = xml.CreateAttribute("start"); + start.InnerText = ((int)Math.Round(p.StartTime.TotalMilliseconds)).ToString(CultureInfo.InvariantCulture); + paragraph.Attributes.Append(start); + + XmlAttribute style = xml.CreateAttribute("style"); + style.InnerText = "Default"; + paragraph.Attributes.Append(style); + + string text = HtmlUtil.RemoveHtmlTags(p.Text); + XmlAttribute textNode = xml.CreateAttribute("text"); + textNode.InnerText = text; + paragraph.Attributes.Append(textNode); + + XmlAttribute translation = xml.CreateAttribute("translation"); + translation.InnerText = string.Empty; + paragraph.Attributes.Append(translation); + + div.AppendChild(paragraph); + } + + return ToUtf8XmlString(xml); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + var xml = new XmlDocument { XmlResolver = null }; + xml.LoadXml(sb.ToString().Trim()); + + XmlNode div = xml.DocumentElement.SelectSingleNode("subtitles"); + foreach (XmlNode node in div.ChildNodes) + { + try + { + // + var p = new Paragraph { StartTime = { TotalMilliseconds = int.Parse(node.Attributes["start"].Value) } }; + p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + int.Parse(node.Attributes["duration"].Value); + p.Text = node.Attributes["text"].Value; + + subtitle.Paragraphs.Add(p); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + _errorCount++; + } + } + subtitle.Renumber(); + } + + } +} diff --git a/libse/SubtitleFormats/SubtitleFormat.cs b/libse/SubtitleFormats/SubtitleFormat.cs new file mode 100644 index 000000000..2cb94a3f2 --- /dev/null +++ b/libse/SubtitleFormats/SubtitleFormat.cs @@ -0,0 +1,400 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public abstract class SubtitleFormat + { + private static IList _allSubtitleFormats; + + /// + /// Formats supported by Subtitle Edit + /// + public static IList AllSubtitleFormats + { + get + { + if (_allSubtitleFormats != null) + return _allSubtitleFormats; + + _allSubtitleFormats = new List + { + new SubRip(), + new AbcIViewer(), + new AdobeAfterEffectsFTME(), + new AdobeEncore(), + new AdobeEncoreLineTabNewLine(), + new AdobeEncoreTabs(), + new AdobeEncoreWithLineNumbers(), + new AdobeEncoreWithLineNumbersNtsc(), + new AdvancedSubStationAlpha(), + new AQTitle(), + new AvidCaption(), + new AvidDvd(), + new BelleNuitSubtitler(), + new CaptionAssistant(), + new Captionate(), + new CaptionateMs(), + new CaraokeXml(), + new Csv(), + new Csv2(), + new Csv3(), + new DCSubtitle(), + new DCinemaSmpte2010(), + new DCinemaSmpte2007(), + new DigiBeta(), + new DvdStudioPro(), + new DvdStudioProSpaceOne(), + new DvdStudioProSpace(), + new DvdSubtitle(), + new DvdSubtitleSystem(), + new Ebu(), + new Eeg708(), + new F4Text(), + new F4Rtf(), + new F4Xml(), + new FabSubtitler(), + new FilmEditXml(), + new FinalCutProXml(), + new FinalCutProXXml(), + new FinalCutProXmlGap(), + new FinalCutProXCM(), + new FinalCutProXml13(), + new FinalCutProXml14(), + new FinalCutProXml14Text(), + new FinalCutProTestXml(), + new FinalCutProTest2Xml(), + new FlashXml(), + new FLVCoreCuePoints(), + new Footage(), + new GpacTtxt(), + new ImageLogicAutocaption(), + new IssXml(), + new ItunesTimedText(), + new Json(), + new JsonType2(), + new JsonType3(), + new JsonType4(), + new JsonType5(), + new JsonType6(), + new Lrc(), + new MicroDvd(), + new MidwayInscriberCGX(), + new MPlayer2(), + new NciTimedRollUpCaptions(), + new OpenDvt(), + new Oresme(), + new OresmeDocXDocument(), + new PE2(), + new PinnacleImpression(), + new PListCaption(), + new QubeMasterImport(), + new QuickTimeText(), + new RealTime(), + new RhozetHarmonic(), + new Sami(), + new SamiModern(), + new SamiYouTube(), + new Scenarist(), + new ScenaristClosedCaptions(), + new ScenaristClosedCaptionsDropFrame(), + new SmilTimesheetData(), + new SoftNiSub(), + new SoftNicolonSub(), + new SonyDVDArchitect(), + new SonyDVDArchitectExplicitDuration(), + new SonyDVDArchitectLineAndDuration(), + new SonyDVDArchitectTabs(), + new SonyDVDArchitectWithLineNumbers(), + new Spruce(), + new SpruceWithSpace(), + new StructuredTitles(), + new SubStationAlpha(), + new SubtitleEditorProject(), + new SubViewer10(), + new SubViewer20(), + new SwiftInterchange2(), + new SwiftText(), + new SwiftTextLineNumber(), + new SwiftTextLineNOAndDur(), + new Tek(), + new TimeXml(), + new TimeXml2(), + new TimedText10(), + new TimedText200604(), + new TimedText200604CData(), + new TimedText(), + new TitleExchangePro(), + new Titra(), + new TmpegEncText(), + new TmpegEncAW5(), + new TmpegEncXml(), + new TMPlayer(), + new TranscriberXml(), + new Tmx14(), + new TurboTitler(), + new UniversalSubtitleFormat(), + new UTSubtitleXml(), + new Utx(), + new UtxFrames(), + new UleadSubtitleFormat(), + new VocapiaSplit(), + new WebVTT(), + new WebVTTFileWithLineNumber(), + new Xif(), + new YouTubeAnnotations(), + new YouTubeSbv(), + new YouTubeTranscript(), + new YouTubeTranscriptOneLine(), + new ZeroG(), + + // new Idx(), + new UnknownSubtitle1(), + new UnknownSubtitle2(), + new UnknownSubtitle3(), + new UnknownSubtitle4(), + new UnknownSubtitle5(), + new UnknownSubtitle6(), + new UnknownSubtitle7(), + new UnknownSubtitle8(), + new UnknownSubtitle9(), + new UnknownSubtitle10(), + new UnknownSubtitle11(), + new UnknownSubtitle12(), + new UnknownSubtitle13(), + new UnknownSubtitle14(), + new UnknownSubtitle15(), + new UnknownSubtitle16(), + new UnknownSubtitle17(), + new UnknownSubtitle18(), + new UnknownSubtitle19(), + new UnknownSubtitle20(), + new UnknownSubtitle21(), + new UnknownSubtitle22(), + new UnknownSubtitle23(), + new UnknownSubtitle24(), + new UnknownSubtitle25(), + new UnknownSubtitle26(), + new UnknownSubtitle27(), + new UnknownSubtitle28(), + new UnknownSubtitle29(), + new UnknownSubtitle30(), + new UnknownSubtitle31(), + new UnknownSubtitle32(), + new UnknownSubtitle33(), + new UnknownSubtitle34(), + new UnknownSubtitle35(), + new UnknownSubtitle36(), + new UnknownSubtitle37(), + new UnknownSubtitle38(), + new UnknownSubtitle39(), + new UnknownSubtitle40(), + new UnknownSubtitle41(), + new UnknownSubtitle42(), + new UnknownSubtitle43(), + new UnknownSubtitle44(), + new UnknownSubtitle45(), + new UnknownSubtitle46(), + new UnknownSubtitle47(), + new UnknownSubtitle48(), + new UnknownSubtitle49(), + new UnknownSubtitle50(), + new UnknownSubtitle51(), + new UnknownSubtitle52(), + new UnknownSubtitle53(), + new UnknownSubtitle54(), + new UnknownSubtitle55(), + new UnknownSubtitle56(), + new UnknownSubtitle57(), + new UnknownSubtitle58(), + new UnknownSubtitle59(), + new UnknownSubtitle60(), + new UnknownSubtitle61(), + new UnknownSubtitle62(), + new UnknownSubtitle63(), + new UnknownSubtitle64(), + new UnknownSubtitle65(), + new UnknownSubtitle66(), + new UnknownSubtitle67(), + new UnknownSubtitle68(), + new UnknownSubtitle69(), + new UnknownSubtitle70(), + new UnknownSubtitle71(), + new UnknownSubtitle72(), + new UnknownSubtitle73(), + new UnknownSubtitle74(), + new UnknownSubtitle75(), + new UnknownSubtitle76(), + new UnknownSubtitle77(), + new UnknownSubtitle78(), + }; + + string path = Configuration.PluginsDirectory; + if (Directory.Exists(path)) + { + string[] pluginFiles = Directory.GetFiles(path, "*.DLL"); + foreach (string pluginFileName in pluginFiles) + { + try + { + var assembly = System.Reflection.Assembly.Load(FileUtil.ReadAllBytesShared(pluginFileName)); + if (assembly != null) + { + foreach (var exportedType in assembly.GetExportedTypes()) + { + try + { + object pluginObject = Activator.CreateInstance(exportedType); + var po = pluginObject as SubtitleFormat; + if (po != null) + _allSubtitleFormats.Insert(1, po); + } + catch + { + } + } + } + } + catch + { + } + } + } + + return _allSubtitleFormats; + } + } + + protected int _errorCount; + + abstract public string Extension + { + get; + } + + abstract public string Name + { + get; + } + + abstract public bool IsTimeBased + { + get; + } + + public bool IsFrameBased + { + get + { + return !IsTimeBased; + } + } + + public string FriendlyName + { + get + { + return string.Format("{0} ({1})", Name, Extension); + } + } + + public int ErrorCount + { + get + { + return _errorCount; + } + } + + abstract public bool IsMine(List lines, string fileName); + + abstract public string ToText(Subtitle subtitle, string title); + + abstract public void LoadSubtitle(Subtitle subtitle, List lines, string fileName); + + public bool IsVobSubIndexFile + { + get + { + return string.CompareOrdinal(Extension, ".idx") == 0; + } + } + + public virtual void RemoveNativeFormatting(Subtitle subtitle, SubtitleFormat newFormat) + { + } + + public virtual List AlternateExtensions + { + get + { + return new List(); + } + } + + public static int MillisecondsToFrames(double milliseconds) + { + return (int)Math.Round(milliseconds / (TimeCode.BaseUnit / Configuration.Settings.General.CurrentFrameRate)); + } + + public static int MillisecondsToFramesMaxFrameRate(double milliseconds) + { + int frames = (int)Math.Round(milliseconds / (TimeCode.BaseUnit / Configuration.Settings.General.CurrentFrameRate)); + if (frames >= Configuration.Settings.General.CurrentFrameRate) + frames = (int)(Configuration.Settings.General.CurrentFrameRate - 0.01); + return frames; + } + + public static int FramesToMilliseconds(double frames) + { + return (int)Math.Round(frames * (TimeCode.BaseUnit / Configuration.Settings.General.CurrentFrameRate)); + } + + public static int FramesToMillisecondsMax999(double frames) + { + int ms = (int)Math.Round(frames * (TimeCode.BaseUnit / Configuration.Settings.General.CurrentFrameRate)); + if (ms > 999) + ms = 999; + return ms; + } + + public virtual bool HasStyleSupport + { + get + { + return false; + } + } + + public bool BatchMode { get; set; } + + public static string ToUtf8XmlString(XmlDocument xml, bool omitXmlDeclaration = false) + { + var settings = new XmlWriterSettings + { + Indent = true, + OmitXmlDeclaration = omitXmlDeclaration, + }; + var result = new StringBuilder(); + + using (var xmlWriter = XmlWriter.Create(result, settings)) + { + xml.Save(xmlWriter); + } + + return result.ToString().Replace(" encoding=\"utf-16\"", " encoding=\"utf-8\"").Trim(); + } + + public virtual bool IsTextBased + { + get + { + return true; + } + } + + } +} diff --git a/libse/SubtitleFormats/SwiftInterchange2.cs b/libse/SubtitleFormats/SwiftInterchange2.cs new file mode 100644 index 000000000..735d05c34 --- /dev/null +++ b/libse/SubtitleFormats/SwiftInterchange2.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class SwiftInterchange2 : SubtitleFormat + { + public override string Extension + { + get { return ".sif"; } + } + + public override string Name + { + get { return "Swift Interchange File V2"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + if (lines.Count > 0 && lines[0] != null && lines[0].StartsWith("{\\rtf1")) + return false; + + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + string date = string.Format("{0:00}/{1:00}/{2}", DateTime.Now.Day, DateTime.Now.Month, DateTime.Now.Year); + const string header = @"# SWIFT INTERCHANGE FILE V2 +# DO NOT EDIT LINES BEGINNING WITH '#' SIGN +# Originating Swift: Line21 30 DROP English (USA) +# VIDEO CLIP : [VIDEO_FILE] +# BROADCAST DATE : [DATE] +# REVISION DATE : [DATE] +# CREATION DATE : [DATE] +# COUNTRY OF ORIGIN : ENG +# EPISODE NUMBER : 0 +# DEADLINE DATE : [DATE] +# AUTO TX : false +# CURRENT STYLE : None +# STYLE DATE : None +# STYLE Time : None +# SUBTITLE [1] RU3 +# TIMEIN 01:00:00:06 +# DURATION 03:21 AUTO +# TIMEOUT --:--:--:-- +# START ROW BOTTOM +# ALIGN CENTRE JUSTIFY LEFT +# ROW 0"; + var sb = new StringBuilder(); + sb.AppendLine(header.Replace("[DATE]", date).Replace("[VIDEO_FILE]", title + ".mpg")); + sb.AppendLine(); + sb.AppendLine(); + const string paragraphWriteFormat = @"# SUBTITLE [{3}] RU3 +# TIMEIN {0} +# DURATION {1} AUTO +# TIMEOUT --:--:--:-- +# START ROW BOTTOM +# ALIGN CENTRE JUSTIFY LEFT +# ROW 0 +{2}"; + int count = 2; + foreach (Paragraph p in subtitle.Paragraphs) + { + string startTime = string.Format("{0:00}:{1:00}:{2:00}.{3:00}", p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, MillisecondsToFramesMaxFrameRate(p.StartTime.Milliseconds)); + string duration = string.Format("{0:00}:{1:00}", p.Duration.Seconds, MillisecondsToFramesMaxFrameRate(p.Duration.Milliseconds)); + sb.AppendLine(string.Format(paragraphWriteFormat, startTime, duration, HtmlUtil.RemoveHtmlTags(p.Text.Replace(Environment.NewLine, " ")), count)); + sb.AppendLine(); + sb.AppendLine(); + count++; + } + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + Paragraph p = null; + foreach (string line in lines) + { + if (line.StartsWith("# SUBTITLE")) + { + if (p != null) + subtitle.Paragraphs.Add(p); + p = new Paragraph(); + } + else if (p != null && line.StartsWith("# TIMEIN")) + { + string timeCode = line.Remove(0, 8).Trim(); + if (timeCode != "--:--:--:--" && !GetTimeCode(p.StartTime, timeCode)) + _errorCount++; + } + else if (p != null && line.StartsWith("# DURATION")) + { + // # DURATION 01:17 AUTO + string timecode = line.Remove(0, 10).Replace("AUTO", string.Empty).Trim(); + if (timecode != "--:--") + { + var arr = timecode.Split(new[] { ':', ' ' }); + if (arr.Length > 1) + { + int sec; + int frame; + if (int.TryParse(arr[0], out sec) && int.TryParse(arr[1], out frame)) + { + p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + FramesToMillisecondsMax999(frame); + p.EndTime.TotalSeconds += sec; + } + } + } + } + else if (p != null && line.StartsWith("# TIMEOUT")) + { + string timeCode = line.Remove(0, 9).Trim(); + if (timeCode != "--:--:--:--" && !GetTimeCode(p.EndTime, timeCode)) + _errorCount++; + } + else if (p != null && !line.StartsWith('#')) + { + if (p.Text.Length > 500) + { + _errorCount += 10; + return; + } + p.Text = (p.Text + Environment.NewLine + line).Trim(); + } + } + if (p != null) + subtitle.Paragraphs.Add(p); + subtitle.RemoveEmptyLines(); + subtitle.Renumber(); + } + + private static bool GetTimeCode(TimeCode timeCode, string timeString) + { + try + { + string[] timeParts = timeString.Split(new[] { ':', '.' }); + timeCode.Hours = int.Parse(timeParts[0]); + timeCode.Minutes = int.Parse(timeParts[1]); + timeCode.Seconds = int.Parse(timeParts[2]); + timeCode.Milliseconds = FramesToMillisecondsMax999(int.Parse(timeParts[3])); + return true; + } + catch + { + return false; + } + } + } +} diff --git a/libse/SubtitleFormats/SwiftText.cs b/libse/SubtitleFormats/SwiftText.cs new file mode 100644 index 000000000..deed19b9d --- /dev/null +++ b/libse/SubtitleFormats/SwiftText.cs @@ -0,0 +1,184 @@ +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class SwiftText : SubtitleFormat + { + private enum ExpectingLine + { + TimeCodes, + Text + } + + private Paragraph _paragraph; + private StringBuilder _text = new StringBuilder(); + private ExpectingLine _expecting = ExpectingLine.TimeCodes; + + private static readonly Regex RegexTimeCodes = new Regex(@"^TIMEIN:\s*[0123456789-]+:[0123456789-]+:[0123456789-]+:[0123456789-]+\s*DURATION:\s*[0123456789-]+:[0123456789-]+\s*TIMEOUT:\s*[0123456789-]+:[0123456789-]+:[0123456789-]+:[0123456789-]+$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Swift text"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + if (lines == null || lines.Count > 2 && !string.IsNullOrEmpty(lines[0]) && lines[0].Contains("{QTtext}")) + return false; + + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + //TIMEIN: 01:00:01:09 DURATION: 01:20 TIMEOUT: --:--:--:-- + //Broadcasting + //from an undisclosed location... + + //TIMEIN: 01:00:04:12 DURATION: 04:25 TIMEOUT: 01:00:09:07 + + const string paragraphWriteFormat = "TIMEIN: {0}\tDURATION: {1}\tTIMEOUT: {2}\r\n{3}\r\n"; + + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + // to avoid rounding errors in duration + var startFrame = MillisecondsToFramesMaxFrameRate(p.StartTime.Milliseconds); + var endFrame = MillisecondsToFramesMaxFrameRate(p.EndTime.Milliseconds); + var durationCalc = new Paragraph( + new TimeCode(p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, FramesToMillisecondsMax999(startFrame)), + new TimeCode(p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, FramesToMillisecondsMax999(endFrame)), + string.Empty); + + string startTime = string.Format("{0:00}:{1:00}:{2:00}:{3:00}", p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, startFrame); + string timeOut = string.Format("{0:00}:{1:00}:{2:00}:{3:00}", p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, endFrame); + string timeDuration = string.Format("{0:00}:{1:00}", durationCalc.Duration.Seconds, MillisecondsToFramesMaxFrameRate(durationCalc.Duration.Milliseconds)); + sb.AppendLine(string.Format(paragraphWriteFormat, startTime, timeDuration, timeOut, p.Text)); + } + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _paragraph = new Paragraph(); + _expecting = ExpectingLine.TimeCodes; + _errorCount = 0; + + subtitle.Paragraphs.Clear(); + foreach (string line in lines) + { + ReadLine(subtitle, line); + if (_text.Length > 1000) + return; + } + if (_text != null && _text.ToString().TrimStart().Length > 0) + { + _paragraph.Text = _text.ToString().Trim(); + subtitle.Paragraphs.Add(_paragraph); + } + + subtitle.Renumber(); + } + + private void ReadLine(Subtitle subtitle, string line) + { + switch (_expecting) + { + case ExpectingLine.TimeCodes: + if (TryReadTimeCodesLine(line, _paragraph)) + { + _text = new StringBuilder(); + _expecting = ExpectingLine.Text; + } + else if (!string.IsNullOrWhiteSpace(line)) + { + _errorCount++; + _expecting = ExpectingLine.Text; // lets go to next paragraph + } + break; + case ExpectingLine.Text: + if (!string.IsNullOrWhiteSpace(line)) + { + _text.AppendLine(line.TrimEnd()); + } + else if (_paragraph != null && _paragraph.EndTime.TotalMilliseconds > 0) + { + _paragraph.Text = _text.ToString().Trim(); + subtitle.Paragraphs.Add(_paragraph); + _paragraph = new Paragraph(); + _expecting = ExpectingLine.TimeCodes; + } + else + { + _errorCount++; + } + break; + } + } + + private static bool TryReadTimeCodesLine(string line, Paragraph paragraph) + { + line = line.Trim(); + if (RegexTimeCodes.IsMatch(line)) + { + //TIMEIN: 01:00:04:12 DURATION: 04:25 TIMEOUT: 01:00:09:07 + string s = line.Replace("TIMEIN:", string.Empty).Replace("DURATION", string.Empty).Replace("TIMEOUT", string.Empty).Replace(" ", string.Empty).Replace("\t", string.Empty); + string[] parts = s.Split(':'); + try + { + int startHours = int.Parse(parts[0]); + int startMinutes = int.Parse(parts[1]); + int startSeconds = int.Parse(parts[2]); + int startMilliseconds = FramesToMillisecondsMax999(int.Parse(parts[3])); + + int durationSeconds = 0; + if (parts[4] != "-") + durationSeconds = int.Parse(parts[4]); + int durationMilliseconds = 0; + if (parts[5] != "--") + durationMilliseconds = FramesToMillisecondsMax999(int.Parse(parts[5])); + + int endHours = 0; + if (parts[6] != "--") + endHours = int.Parse(parts[6]); + int endMinutes = 0; + if (parts[7] != "--") + endMinutes = int.Parse(parts[7]); + int endSeconds = 0; + if (parts[8] != "--") + endSeconds = int.Parse(parts[8]); + int endMilliseconds = 0; + if (parts[9] != "--") + endMilliseconds = FramesToMillisecondsMax999(int.Parse(parts[9])); + + paragraph.StartTime = new TimeCode(startHours, startMinutes, startSeconds, startMilliseconds); + + if (durationSeconds > 0 || durationMilliseconds > 0) + paragraph.EndTime.TotalMilliseconds = paragraph.StartTime.TotalMilliseconds + (durationSeconds * 1000 + durationMilliseconds); + else + paragraph.EndTime = new TimeCode(endHours, endMinutes, endSeconds, endMilliseconds); + return true; + } + catch + { + return false; + } + } + return false; + } + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/SwiftTextLineNoAndDur.cs b/libse/SubtitleFormats/SwiftTextLineNoAndDur.cs new file mode 100644 index 000000000..98a1b437f --- /dev/null +++ b/libse/SubtitleFormats/SwiftTextLineNoAndDur.cs @@ -0,0 +1,181 @@ +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class SwiftTextLineNOAndDur : SubtitleFormat + { + private enum ExpectingLine + { + TimeCodes, + Text + } + + private Paragraph _paragraph; + private StringBuilder _text = new StringBuilder(); + private ExpectingLine _expecting = ExpectingLine.TimeCodes; + + private static readonly Regex RegexTimeCodes = new Regex(@"^SUBTITLE: \d+\s+TIMEIN:\s*[0123456789-]+:[0123456789-]+:[0123456789-]+:[0123456789-]+\s*DURATION:\s*[0123456789-]+:[0123456789-]+\s+TIMEOUT:\s*[0123456789-]+:[0123456789-]+:[0123456789-]+:[0123456789-]+$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Swift text line# +dur"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + if (lines != null && lines.Count > 2 && !string.IsNullOrEmpty(lines[0]) && lines[0].Contains("{QTtext}")) + return false; + + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + //SUBTITLE: 1 TIMEIN: 00:00:07:01 DURATION: 03:11 TIMEOUT: 00:00:10:12 + //Voor de oorlog + + //SUBTITLE: 2 TIMEIN: 00:00:10:16 DURATION: 01:08 TIMEOUT: 00:00:11:24 + //Ik ben Marie Pinhas. Ik ben geboren + //in Thessaloniki in Griekenland, + + //SUBTITLE: 3 TIMEIN: 00:00:12:12 DURATION: 02:10 TIMEOUT: 00:00:14:22 + //op 6 maart '31, + //in een heel oude Griekse familie. + + const string paragraphWriteFormat = "SUBTITLE: {1}\tTIMEIN: {0}\tDURATION: {4}\tTIMEOUT: {2}\r\n{3}\r\n"; + + var sb = new StringBuilder(); + int count = 1; + foreach (Paragraph p in subtitle.Paragraphs) + { + // to avoid rounding errors in duration + var startFrame = MillisecondsToFramesMaxFrameRate(p.StartTime.Milliseconds); + var endFrame = MillisecondsToFramesMaxFrameRate(p.EndTime.Milliseconds); + var durationCalc = new Paragraph( + new TimeCode(p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, FramesToMillisecondsMax999(startFrame)), + new TimeCode(p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, FramesToMillisecondsMax999(endFrame)), + string.Empty); + + string startTime = string.Format("{0:00}:{1:00}:{2:00}:{3:00}", p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, startFrame); + string timeOut = string.Format("{0:00}:{1:00}:{2:00}:{3:00}", p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, endFrame); + string timeDuration = string.Format("{0:00}:{1:00}", durationCalc.Duration.Seconds, MillisecondsToFramesMaxFrameRate(durationCalc.Duration.Milliseconds)); + sb.AppendLine(string.Format(paragraphWriteFormat, startTime, count, timeOut, p.Text, timeDuration)); + count++; + } + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _paragraph = new Paragraph(); + _expecting = ExpectingLine.TimeCodes; + _errorCount = 0; + + subtitle.Paragraphs.Clear(); + foreach (string line in lines) + { + ReadLine(subtitle, line); + if (_text.Length > 1000) + return; + } + if (_text != null && _text.ToString().TrimStart().Length > 0) + { + _paragraph.Text = _text.ToString().Trim(); + subtitle.Paragraphs.Add(_paragraph); + } + + subtitle.Renumber(); + } + + private void ReadLine(Subtitle subtitle, string line) + { + switch (_expecting) + { + case ExpectingLine.TimeCodes: + if (TryReadTimeCodesLine(line, _paragraph)) + { + _text = new StringBuilder(); + _expecting = ExpectingLine.Text; + } + else if (!string.IsNullOrWhiteSpace(line)) + { + _errorCount++; + _expecting = ExpectingLine.Text; // lets go to next paragraph + } + break; + case ExpectingLine.Text: + if (!string.IsNullOrWhiteSpace(line)) + { + _text.AppendLine(line.TrimEnd()); + } + else if (_paragraph != null && _paragraph.EndTime.TotalMilliseconds > 0) + { + _paragraph.Text = _text.ToString().Trim(); + subtitle.Paragraphs.Add(_paragraph); + _paragraph = new Paragraph(); + _expecting = ExpectingLine.TimeCodes; + } + else + { + _errorCount++; + } + break; + } + } + + private static bool TryReadTimeCodesLine(string line, Paragraph paragraph) + { + line = line.Trim(); + if (RegexTimeCodes.IsMatch(line)) + { + //SUBTITLE: 1 TIMEIN: 00:00:07:01 DURATION: 03:11 TIMEOUT: 00:00:10:12 + string s = line.Replace("SUBTITLE:", string.Empty).Replace("TIMEIN", string.Empty).Replace("DURATION", string.Empty).Replace("TIMEOUT", string.Empty).Replace(" ", string.Empty).Replace("\t", string.Empty); + string[] parts = s.Split(':'); + try + { + int startHours = int.Parse(parts[1]); + int startMinutes = int.Parse(parts[2]); + int startSeconds = int.Parse(parts[3]); + int startMilliseconds = FramesToMillisecondsMax999(int.Parse(parts[4])); + + int endHours = 0; + if (parts[5 + 2] != "--") + endHours = int.Parse(parts[5 + 2]); + int endMinutes = 0; + if (parts[6 + 2] != "--") + endMinutes = int.Parse(parts[6 + 2]); + int endSeconds = 0; + if (parts[7 + 2] != "--") + endSeconds = int.Parse(parts[7 + 2]); + int endMilliseconds = 0; + if (parts[8 + 2] != "--") + endMilliseconds = FramesToMillisecondsMax999(int.Parse(parts[8 + 2])); + + paragraph.StartTime = new TimeCode(startHours, startMinutes, startSeconds, startMilliseconds); + paragraph.EndTime = new TimeCode(endHours, endMinutes, endSeconds, endMilliseconds); + return true; + } + catch + { + return false; + } + } + return false; + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/SwiftTextLineNumber.cs b/libse/SubtitleFormats/SwiftTextLineNumber.cs new file mode 100644 index 000000000..cc0ef6cf5 --- /dev/null +++ b/libse/SubtitleFormats/SwiftTextLineNumber.cs @@ -0,0 +1,171 @@ +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class SwiftTextLineNumber : SubtitleFormat + { + private enum ExpectingLine + { + TimeCodes, + Text + } + + private Paragraph _paragraph; + private StringBuilder _text = new StringBuilder(); + private ExpectingLine _expecting = ExpectingLine.TimeCodes; + + private static readonly Regex RegexTimeCodes = new Regex(@"^SUBTITLE: \d+\s+TIMEIN:\s*[0123456789-]+:[0123456789-]+:[0123456789-]+:[0123456789-]+\s*TIMEOUT:\s*[0123456789-]+:[0123456789-]+:[0123456789-]+:[0123456789-]+$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Swift text line#"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + if (lines == null || lines.Count > 2 && !string.IsNullOrEmpty(lines[0]) && lines[0].Contains("{QTtext}")) + return false; + + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + //SUBTITLE: 1 TIMEIN: 00:00:00:00 TIMEOUT: 00:00:04:00 + //Voor de oorlog + + //SUBTITLE: 2 TIMEIN: 00:00:05:12 TIMEOUT: 00:00:10:02 + //Ik ben Marie Pinhas. Ik ben geboren + //in Thessaloniki in Griekenland, + + //SUBTITLE: 3 TIMEIN: 00:00:10:06 TIMEOUT: 00:00:15:17 + //op 6 maart '31, + //in een heel oude Griekse familie. + + const string paragraphWriteFormat = "SUBTITLE: {1}\tTIMEIN: {0}\tTIMEOUT: {2}\r\n{3}\r\n"; + + var sb = new StringBuilder(); + int count = 1; + foreach (Paragraph p in subtitle.Paragraphs) + { + string startTime = string.Format("{0:00}:{1:00}:{2:00}:{3:00}", p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, MillisecondsToFramesMaxFrameRate(p.StartTime.Milliseconds)); + string timeOut = string.Format("{0:00}:{1:00}:{2:00}:{3:00}", p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, MillisecondsToFramesMaxFrameRate(p.EndTime.Milliseconds)); + sb.AppendLine(string.Format(paragraphWriteFormat, startTime, count, timeOut, p.Text)); + count++; + } + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _paragraph = new Paragraph(); + _expecting = ExpectingLine.TimeCodes; + _errorCount = 0; + + subtitle.Paragraphs.Clear(); + foreach (string line in lines) + { + ReadLine(subtitle, line); + if (_text.Length > 1000) + return; + } + if (_text != null && _text.ToString().TrimStart().Length > 0) + { + _paragraph.Text = _text.ToString().Trim(); + subtitle.Paragraphs.Add(_paragraph); + } + + subtitle.Renumber(); + } + + private void ReadLine(Subtitle subtitle, string line) + { + switch (_expecting) + { + case ExpectingLine.TimeCodes: + if (TryReadTimeCodesLine(line, _paragraph)) + { + _text = new StringBuilder(); + _expecting = ExpectingLine.Text; + } + else if (!string.IsNullOrWhiteSpace(line)) + { + _errorCount++; + _expecting = ExpectingLine.Text; // lets go to next paragraph + } + break; + case ExpectingLine.Text: + if (!string.IsNullOrWhiteSpace(line)) + { + _text.AppendLine(line.TrimEnd()); + } + else if (_paragraph != null && _paragraph.EndTime.TotalMilliseconds > 0) + { + _paragraph.Text = _text.ToString().Trim(); + subtitle.Paragraphs.Add(_paragraph); + _paragraph = new Paragraph(); + _expecting = ExpectingLine.TimeCodes; + } + else + { + _errorCount++; + } + break; + } + } + + private static bool TryReadTimeCodesLine(string line, Paragraph paragraph) + { + line = line.Trim(); + if (RegexTimeCodes.IsMatch(line)) + { + //SUBTITLE: 59 TIMEIN: 00:04:28:06 TIMEOUT: 00:04:32:12 + string s = line.Replace("SUBTITLE:", string.Empty).Replace("TIMEIN", string.Empty).Replace("TIMEOUT", string.Empty).Replace(" ", string.Empty).Replace("\t", string.Empty); + string[] parts = s.Split(':'); + try + { + int startHours = int.Parse(parts[1]); + int startMinutes = int.Parse(parts[2]); + int startSeconds = int.Parse(parts[3]); + int startMilliseconds = FramesToMillisecondsMax999(int.Parse(parts[4])); + + int endHours = 0; + if (parts[5] != "--") + endHours = int.Parse(parts[5]); + int endMinutes = 0; + if (parts[6] != "--") + endMinutes = int.Parse(parts[6]); + int endSeconds = 0; + if (parts[7] != "--") + endSeconds = int.Parse(parts[7]); + int endMilliseconds = 0; + if (parts[8] != "--") + endMilliseconds = FramesToMillisecondsMax999(int.Parse(parts[8])); + + paragraph.StartTime = new TimeCode(startHours, startMinutes, startSeconds, startMilliseconds); + paragraph.EndTime = new TimeCode(endHours, endMinutes, endSeconds, endMilliseconds); + return true; + } + catch + { + return false; + } + } + return false; + } + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/TMPlayer.cs b/libse/SubtitleFormats/TMPlayer.cs new file mode 100644 index 000000000..6f7e2e536 --- /dev/null +++ b/libse/SubtitleFormats/TMPlayer.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + + public class TMPlayer : SubtitleFormat + { + private static readonly Regex regex = new Regex(@"^\d+:\d\d:\d\d[: ].*$", RegexOptions.Compiled); // accept a " " instead of the last ":" too + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "TMPlayer"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + + if (subtitle.Paragraphs.Count > 4) + { + bool allStartWithNumber = true; + foreach (Paragraph p in subtitle.Paragraphs) + { + if (p.Text.Length > 1 && !Utilities.IsInteger(p.Text.Substring(0, 2))) + { + allStartWithNumber = false; + break; + } + } + if (allStartWithNumber) + return false; + } + if (subtitle.Paragraphs.Count > _errorCount) + { + if (new UnknownSubtitle33().IsMine(lines, fileName) || new UnknownSubtitle36().IsMine(lines, fileName)) + return false; + return true; + } + return false; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + string text = HtmlUtil.RemoveHtmlTags(p.Text); + text = text.Replace(Environment.NewLine, "|"); + sb.AppendLine(string.Format("{0:00}:{1:00}:{2:00}:{3}", p.StartTime.Hours, + p.StartTime.Minutes, + p.StartTime.Seconds, + text)); + } + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { // 0:02:36:You've returned to the village|after 2 years, Shekhar. + // 00:00:50:America has made my fortune. + _errorCount = 0; + foreach (string line in lines) + { + bool success = false; + if (line.IndexOf(':') > 0 && regex.Match(line).Success) + { + try + { + string s = line; + if (line.Length > 9 && line[8] == ' ') + s = line.Substring(0, 8) + ":" + line.Substring(9); + string[] parts = s.Split(':'); + if (parts.Length > 3) + { + int hours = int.Parse(parts[0]); + int minutes = int.Parse(parts[1]); + int seconds = int.Parse(parts[2]); + string text = string.Empty; + for (int i = 3; i < parts.Length; i++) + { + if (text.Length == 0) + text = parts[i]; + else + text += ":" + parts[i]; + } + text = text.Replace("|", Environment.NewLine); + var start = new TimeCode(hours, minutes, seconds, 0); + double duration = Utilities.GetOptimalDisplayMilliseconds(text); + var end = new TimeCode(start.TotalMilliseconds + duration); + + var p = new Paragraph(start, end, text); + subtitle.Paragraphs.Add(p); + success = true; + } + } + catch + { + _errorCount++; + } + } + if (!success) + _errorCount++; + } + + int index = 0; + foreach (Paragraph p in subtitle.Paragraphs) + { + Paragraph next = subtitle.GetParagraphOrDefault(index + 1); + if (next != null && next.StartTime.TotalMilliseconds <= p.EndTime.TotalMilliseconds) + p.EndTime.TotalMilliseconds = next.StartTime.TotalMilliseconds - 1; + + index++; + p.Number = index; + } + } + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/TSB4.cs b/libse/SubtitleFormats/TSB4.cs new file mode 100644 index 000000000..2e72b8719 --- /dev/null +++ b/libse/SubtitleFormats/TSB4.cs @@ -0,0 +1,92 @@ +using System.Collections.Generic; +using System.Text; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class TSB4 : SubtitleFormat + { + public override string Extension + { + get { return ".sub"; } + } + + public override string Name + { + get { return "TSB4"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > this._errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + return "Not supported!"; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + subtitle.Paragraphs.Clear(); + if (string.IsNullOrEmpty(fileName)) + { + return; + } + byte[] array; + try + { + array = FileUtil.ReadAllBytesShared(fileName); + } + catch + { + _errorCount++; + return; + } + if (array.Length < 100 || array[0] != 84 || array[1] != 83 || array[2] != 66 || array[3] != 52) + { + return; + } + for (int i = 0; i < array.Length - 20; i++) + { + if (array[i] == 84 && array[i + 1] == 73 && array[i + 2] == 84 && array[i + 3] == 76 && array[i + 8] == 84 && array[i + 9] == 73 && array[i + 10] == 77 && array[i + 11] == 69) // TITL + TIME + { + int endOfText = array[i + 4]; + + int start = array[i + 16] + array[i + 17] * 256; + if (array[i + 18] != 32) + start += array[i + 18] * 256 * 256; + + int end = array[i + 20] + array[i + 21] * 256; + if (array[i + 22] != 32) + end += array[i + 22] * 256 * 256; + + int textStart = i; + while (textStart < i + endOfText && !(array[textStart] == 0x4C && array[textStart + 1] == 0x49 && array[textStart + 2] == 0x4E && array[textStart + 3] == 0x45)) // LINE + { + textStart++; + } + int length = i + endOfText - textStart - 2; + textStart += 8; + + string text = Encoding.Default.GetString(array, textStart, length); + // text = Encoding.Default.GetString(array, i + 53, endOfText - 47); + text = text.Trim('\0').Replace("\0", " ").Trim(); + var item = new Paragraph(text, FramesToMilliseconds(start), FramesToMilliseconds(end)); + subtitle.Paragraphs.Add(item); + i += endOfText + 5; + } + } + subtitle.RemoveEmptyLines(); + subtitle.Renumber(); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/Tek.cs b/libse/SubtitleFormats/Tek.cs new file mode 100644 index 000000000..4997eb5b8 --- /dev/null +++ b/libse/SubtitleFormats/Tek.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class Tek : SubtitleFormat + { + private static readonly Regex RegexTimeCode = new Regex(@"^\d+ \d+ \d \d \d$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".tek"; } + } + + public override string Name + { + get { return "TEK"; } + } + + public override bool IsTimeBased + { + get { return false; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + //1. + //8.03 + //10.06 + //- Labai aèiû. + //- Jûs rimtai? + + //2. + //16.00 + //19.06 + //Kaip reikalai ðunø grobimo versle? + + const string paragraphWriteFormat = "{0} {1} 1 1 0\r\n{2}"; + var sb = new StringBuilder(); + sb.AppendLine(@"ý Smart Titl Editor / Smart Titler (A)(C)1992-2001. Dragutin Nikolic +ý Serial No: XXXXXXXXXXXXXX +ý Korisnik: Prava i Prevodi - prevodioci +ý +ý KONFIGURACIONI PODACI +ý Dozvoljeno slova u redu: 30 +ý Vremenska korekcija: 1.0000000000E+00 +ý Radjeno vremenskih korekcija: TRUE +ý Slovni raspored ASCIR +ý +ý Kraj info blocka."); + sb.AppendLine(); + int count = 0; + + if (!subtitle.WasLoadedWithFrameNumbers) + subtitle.CalculateFrameNumbersFromTimeCodes(Configuration.Settings.General.CurrentFrameRate); + foreach (Paragraph p in subtitle.Paragraphs) + { + count++; + var text = HtmlUtil.RemoveOpenCloseTags(p.Text, HtmlUtil.TagFont); + sb.AppendLine(string.Format(paragraphWriteFormat, p.StartFrame, p.EndFrame, text)); + } + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + Paragraph paragraph = null; + _errorCount = 0; + + subtitle.Paragraphs.Clear(); + foreach (string line in lines) + { + string s = line.Trim(); + if (RegexTimeCode.IsMatch(s)) + { + if (paragraph != null) + subtitle.Paragraphs.Add(paragraph); + paragraph = new Paragraph(); + string[] parts = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 5) + { + try + { + paragraph.StartFrame = int.Parse(parts[0]); + paragraph.EndFrame = int.Parse(parts[1]); + paragraph.CalculateTimeCodesFromFrameNumbers(Configuration.Settings.General.CurrentFrameRate); + } + catch + { + _errorCount++; + } + } + } + else if (paragraph != null && s.Length > 0) + { + paragraph.Text = (paragraph.Text + Environment.NewLine + s).Trim(); + if (paragraph.Text.Length > 2000) + { + _errorCount += 100; + return; + } + } + else if (s.Length > 0 && !s.StartsWith('ý')) + { + _errorCount++; + } + } + if (paragraph != null) + subtitle.Paragraphs.Add(paragraph); + subtitle.Renumber(); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/TextST.cs b/libse/SubtitleFormats/TextST.cs new file mode 100644 index 000000000..47b023bd1 --- /dev/null +++ b/libse/SubtitleFormats/TextST.cs @@ -0,0 +1,1138 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Text; +using Nikse.SubtitleEdit.Core.BluRaySup; +using Nikse.SubtitleEdit.Core.TransportStream; +using Nikse.SubtitleEdit.Core.VobSub; +using Helper = Nikse.SubtitleEdit.Core.TransportStream.Helper; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public static class StreamExtensions + { + + public static void WritePts(this Stream stream, ulong pts) + { + //TODO: check max + var buffer = BitConverter.GetBytes(pts); + if (BitConverter.IsLittleEndian) + { + stream.WriteByte(buffer[4]); + stream.WriteByte(buffer[3]); + stream.WriteByte(buffer[2]); + stream.WriteByte(buffer[1]); + stream.WriteByte(buffer[0]); + } + else + { + stream.WriteByte(buffer[buffer.Length - 1]); + stream.WriteByte(buffer[buffer.Length - 2]); + stream.WriteByte(buffer[buffer.Length - 3]); + stream.WriteByte(buffer[buffer.Length - 4]); + stream.WriteByte(buffer[buffer.Length - 5]); + } + } + + public static void WriteWord(this Stream stream, int value) + { + //TODO: check max + stream.WriteByte((byte)(value / 256)); + stream.WriteByte((byte)(value % 256)); + } + + public static void WriteWord(this Stream stream, int value, int firstBitValue) + { + //TODO: check max + var firstByte = (byte)(value / 256); + if (firstBitValue == 1) + firstByte = (byte)(firstByte | Helper.B10000000); + stream.WriteByte(firstByte); + stream.WriteByte((byte)(value % 256)); + } + + public static void WriteByte(this Stream stream, int value, int firstBitValue) + { + //TODO: check max + var firstByte = (byte)(value); + if (firstBitValue == 1) + firstByte = (byte)(firstByte | Helper.B10000000); + stream.WriteByte(firstByte); + } + } + + public class TextST : SubtitleFormat + { + + public class Palette + { + public int PaletteEntryId { get; set; } + public int Y { get; set; } + public int Cr { get; set; } + public int Cb { get; set; } + public int T { get; set; } + + public Color Color + { + get + { + var arr = BluRaySupPalette.YCbCr2Rgb(Y, Cb, Cr, false); + return Color.FromArgb(T, arr[0], arr[1], arr[2]); + } + } + } + + public class RegionStyle + { + public int RegionStyleId { get; set; } + public int RegionHorizontalPosition { get; set; } + public int RegionVerticalPosition { get; set; } + public int RegionWidth { get; set; } + public int RegionHeight { get; set; } + public int RegionBgPaletteEntryIdRef { get; set; } + public int TextBoxHorizontalPosition { get; set; } + public int TextBoxVerticalPosition { get; set; } + public int TextBoxWidth { get; set; } + public int TextBoxHeight { get; set; } + public int TextFlow { get; set; } + public int TextHorizontalAlignment { get; set; } + public int TextVerticalAlignment { get; set; } + public int LineSpace { get; set; } + public int FontIdRef { get; set; } + public int FontStyle { get; set; } + public int FontSize { get; set; } + public int FontPaletteEntryIdRef { get; set; } + public int FontOutlinePaletteEntryIdRef { get; set; } + public int FontOutlineThickness { get; set; } + } + + public class UserStyle + { + public int UserStyleId { get; set; } + public int RegionHorizontalPositionDirection { get; set; } + public int RegionHorizontalPositionDelta { get; set; } + public int RegionVerticalPositionDirection { get; set; } + public int RegionVerticalPositionDelta { get; set; } + public int FontSizeIncDec { get; set; } + public int FontSizeDelta { get; set; } + public int TextBoxHorizontalPositionDirection { get; set; } + public int TextBoxHorizontalPositionDelta { get; set; } + public int TextBoxVerticalPositionDirection { get; set; } + public int TextBoxVerticalPositionDelta { get; set; } + public int TextBoxWidthIncDec { get; set; } + public int TextBoxWidthDelta { get; set; } + public int TextBoxHeightIncDec { get; set; } + public int TextBoxHeightDelta { get; set; } + public int LineSpaceIncDec { get; set; } + public int LineSpaceDelta { get; set; } + } + + public class DialogStyleSegment + { + public bool PlayerStyleFlag { get; set; } + public int NumberOfRegionStyles { get; set; } + public int NumberOfUserStyles { get; set; } + public List RegionStyles { get; set; } + public List UserStyles { get; set; } + public List Palettes { get; set; } + public int NumberOfDialogPresentationSegments { get; set; } + + public DialogStyleSegment() + { + PlayerStyleFlag = true; + RegionStyles = new List(); + UserStyles = new List(); + Palettes = new List(); + } + + public DialogStyleSegment(byte[] buffer) + { + PlayerStyleFlag = (buffer[9] & Helper.B10000000) > 0; + NumberOfRegionStyles = buffer[11]; + NumberOfUserStyles = buffer[12]; + + int idx = 13; + RegionStyles = new List(NumberOfRegionStyles); + for (int i = 0; i < NumberOfRegionStyles; i++) + { + var rs = new RegionStyle + { + RegionStyleId = buffer[idx], + RegionHorizontalPosition = (buffer[idx + 1] << 8) + buffer[idx + 2], + RegionVerticalPosition = (buffer[idx + 3] << 8) + buffer[idx + 4], + RegionWidth = (buffer[idx + 5] << 8) + buffer[idx + 6], + RegionHeight = (buffer[idx + 7] << 8) + buffer[idx + 8], + RegionBgPaletteEntryIdRef = buffer[idx + 9], + TextBoxHorizontalPosition = (buffer[idx + 11] << 8) + buffer[idx + 12], + TextBoxVerticalPosition = (buffer[idx + 13] << 8) + buffer[idx + 14], + TextBoxWidth = (buffer[idx + 15] << 8) + buffer[idx + 16], + TextBoxHeight = (buffer[idx + 17] << 8) + buffer[idx + 18], + TextFlow = buffer[idx + 19], + TextHorizontalAlignment = buffer[idx + 20], + TextVerticalAlignment = buffer[idx + 21], + LineSpace = buffer[idx + 22], + FontIdRef = buffer[idx + 23], + FontStyle = buffer[idx + 24], + FontSize = buffer[idx + 25], + FontPaletteEntryIdRef = buffer[idx + 26], + FontOutlinePaletteEntryIdRef = buffer[idx + 27], + FontOutlineThickness = buffer[idx + 28] + }; + RegionStyles.Add(rs); + idx += 29; + } + + UserStyles = new List(); + for (int j = 0; j < NumberOfUserStyles; j++) + { + var us = new UserStyle + { + UserStyleId = buffer[idx], + RegionHorizontalPositionDirection = buffer[idx + 1] >> 7, + RegionHorizontalPositionDelta = ((buffer[idx + 1] & Helper.B01111111) << 8) + buffer[idx + 2], + RegionVerticalPositionDirection = buffer[idx + 3] >> 7, + RegionVerticalPositionDelta = ((buffer[idx + 3] & Helper.B01111111) << 8) + buffer[idx + 4], + FontSizeIncDec = buffer[idx + 5] >> 7, + FontSizeDelta = (buffer[idx + 5] & Helper.B01111111), + TextBoxHorizontalPositionDirection = buffer[idx + 6] >> 7, + TextBoxHorizontalPositionDelta = ((buffer[idx + 6] & Helper.B01111111) << 8) + buffer[idx + 7], + TextBoxVerticalPositionDirection = buffer[idx + 8] >> 7, + TextBoxVerticalPositionDelta = ((buffer[idx + 8] & Helper.B01111111) << 8) + buffer[idx + 9], + TextBoxWidthIncDec = buffer[idx + 10] >> 7, + TextBoxWidthDelta = ((buffer[idx + 10] & Helper.B01111111) << 8) + buffer[idx + 11], + TextBoxHeightIncDec = buffer[idx + 12] >> 7, + TextBoxHeightDelta = ((buffer[idx + 12] & Helper.B01111111) << 8) + buffer[idx + 13], + LineSpaceIncDec = buffer[idx + 14] >> 7, + LineSpaceDelta = (buffer[idx + 14] & Helper.B01111111) + }; + UserStyles.Add(us); + idx += 15; + } + + int numberOfPalettees = ((buffer[idx] << 8) + buffer[idx + 1]) / 5; + Palettes = new List(numberOfPalettees); + idx += 2; + for (int i = 0; i < numberOfPalettees; i++) + { + var palette = new Palette + { + PaletteEntryId = buffer[idx], + Y = buffer[idx + 1], + Cr = buffer[idx + 2], + Cb = buffer[idx + 3], + T = buffer[idx + 4] + }; + Palettes.Add(palette); + idx += 5; + } + NumberOfDialogPresentationSegments = (buffer[idx] << 8) + buffer[idx + 1]; + } + + public void WriteToStream(Stream stream, int numberOfSubtitles) + { + byte[] regionStyle = MakeRegionStyle(); + stream.Write(new byte[] { 0, 0, 1, 0xbf }, 0, 4); // MPEG-2 Private stream 2 + var size = regionStyle.Length + 5; + stream.WriteWord(size); + stream.WriteByte(SegmentTypeDialogStyle); // 0x81 + stream.WriteWord(size - 3); + stream.Write(regionStyle, 0, regionStyle.Length); + stream.WriteWord(numberOfSubtitles); + } + + private byte[] MakeRegionStyle() + { + using (var ms = new MemoryStream()) + { + if (PlayerStyleFlag) + ms.WriteByte(Helper.B10000000); + else + ms.WriteByte(0); + ms.WriteByte(0); // reserved? + ms.WriteByte((byte)NumberOfRegionStyles); + ms.WriteByte((byte)NumberOfUserStyles); + + foreach (var regionStyle in RegionStyles) + { + AddRegionStyle(ms, regionStyle); + } + + foreach (var userStyle in UserStyles) + { + AddUserStyle(ms, userStyle); + } + + ms.WriteWord(Palettes.Count * 5); + foreach (var palette in Palettes) + { + ms.WriteByte((byte)palette.PaletteEntryId); + ms.WriteByte((byte)palette.Y); + ms.WriteByte((byte)palette.Cb); + ms.WriteByte((byte)palette.Cr); + ms.WriteByte((byte)palette.T); + } + + return ms.ToArray(); + } + } + + private void AddUserStyle(Stream stream, UserStyle userStyle) + { + stream.WriteByte((byte)userStyle.UserStyleId); + stream.WriteWord(userStyle.RegionHorizontalPositionDelta, userStyle.RegionHorizontalPositionDirection); + stream.WriteWord(userStyle.RegionVerticalPositionDelta, userStyle.RegionVerticalPositionDirection); + stream.WriteByte(userStyle.FontSizeDelta, userStyle.FontSizeIncDec); + stream.WriteWord(userStyle.TextBoxHorizontalPositionDelta, userStyle.TextBoxHorizontalPositionDirection); + stream.WriteWord(userStyle.TextBoxVerticalPositionDelta, userStyle.TextBoxVerticalPositionDirection); + stream.WriteWord(userStyle.TextBoxWidthDelta, userStyle.TextBoxWidthIncDec); + stream.WriteWord(userStyle.TextBoxHeightDelta, userStyle.TextBoxHeightIncDec); + stream.WriteByte(userStyle.LineSpaceDelta, userStyle.LineSpaceIncDec); + } + + private void AddRegionStyle(Stream stream, RegionStyle regionStyle) + { + stream.WriteByte((byte)regionStyle.RegionStyleId); + stream.WriteWord(regionStyle.RegionHorizontalPosition); + stream.WriteWord(regionStyle.RegionVerticalPosition); + stream.WriteWord(regionStyle.RegionWidth); + stream.WriteWord(regionStyle.RegionHeight); + stream.WriteByte((byte)regionStyle.RegionBgPaletteEntryIdRef); + stream.WriteByte(0); // reserved + stream.WriteWord(regionStyle.TextBoxHorizontalPosition); + stream.WriteWord(regionStyle.TextBoxVerticalPosition); + stream.WriteWord(regionStyle.TextBoxWidth); + stream.WriteWord(regionStyle.TextBoxHeight); + stream.WriteByte((byte)regionStyle.TextFlow); + stream.WriteByte((byte)regionStyle.TextHorizontalAlignment); + stream.WriteByte((byte)regionStyle.TextVerticalAlignment); + stream.WriteByte((byte)regionStyle.LineSpace); + stream.WriteByte((byte)regionStyle.FontIdRef); + stream.WriteByte((byte)regionStyle.FontStyle); + stream.WriteByte((byte)regionStyle.FontSize); + stream.WriteByte((byte)regionStyle.FontPaletteEntryIdRef); + stream.WriteByte((byte)regionStyle.FontOutlinePaletteEntryIdRef); + stream.WriteByte((byte)regionStyle.FontOutlineThickness); + } + + public static DialogStyleSegment DefaultDialogStyleSegment + { + get + { + var dss = new DialogStyleSegment(); + + dss.RegionStyles.Add(new RegionStyle + { + RegionStyleId = 0, + RegionHorizontalPosition = 100, + RegionVerticalPosition = 880, + RegionWidth = 1720, + RegionHeight = 200, + RegionBgPaletteEntryIdRef = 2, + TextBoxHorizontalPosition = 0, + TextBoxVerticalPosition = 880, + TextBoxWidth = 1719, + TextBoxHeight = 130, + TextFlow = 1, + TextHorizontalAlignment = 2, + TextVerticalAlignment = 1, + LineSpace = 70, + FontIdRef = 0, + FontStyle = 4, + FontSize = 45, + FontPaletteEntryIdRef = 3, + FontOutlinePaletteEntryIdRef = 1, + FontOutlineThickness = 2, + }); + dss.NumberOfRegionStyles = dss.RegionStyles.Count; + + dss.Palettes.Add(new Palette + { + PaletteEntryId = 0, + Y = 235, + Cr = 128, + Cb = 128, + T = 0 + }); + dss.Palettes.Add(new Palette + { + PaletteEntryId = 1, + Y = 16, + Cr = 128, + Cb = 128, + T = 255 + }); + dss.Palettes.Add(new Palette + { + PaletteEntryId = 2, + Y = 235, + Cr = 128, + Cb = 128, + T = 0 + }); + dss.Palettes.Add(new Palette + { + PaletteEntryId = 3, + Y = 235, + Cr = 128, + Cb = 128, + T = 255 + }); + dss.Palettes.Add(new Palette + { + PaletteEntryId = 254, + Y = 16, + Cr = 128, + Cb = 128, + T = 0 + }); + return dss; + } + } + } + + + public abstract class SubtitleRegionContent + { + public int EscapeCode { get; set; } + public int DataType { get; set; } + public int DataLength { get; set; } + public string Name { get; set; } + public abstract void WriteExtraToStream(Stream stream); + } + + public class SubtitleRegionContentText : SubtitleRegionContent + { + private string _text; + + public string Text + { + get { return _text; } + set + { + DataLength = Encoding.UTF8.GetBytes(value).Length; + _text = value; + } + } + + public SubtitleRegionContentText() + { + EscapeCode = 27; + DataType = 1; + Name = "Text"; + } + + public override void WriteExtraToStream(Stream stream) + { + var buffer = Encoding.UTF8.GetBytes(Text); + stream.Write(buffer, 0, buffer.Length); + } + } + + public class SubtitleRegionContentChangeFontSet : SubtitleRegionContent + { + public int FontId { get; set; } + + public SubtitleRegionContentChangeFontSet() + { + EscapeCode = 27; + DataType = 2; + DataLength = 1; + Name = "Font set"; + } + + public override void WriteExtraToStream(Stream stream) + { + stream.WriteByte((byte)FontId); + } + } + + public class SubtitleRegionContentChangeFontStyle : SubtitleRegionContent + { + public int FontStyle { get; set; } + public int FontOutlinePaletteId { get; set; } + public int FontOutlineThickness { get; set; } + + public SubtitleRegionContentChangeFontStyle() + { + EscapeCode = 27; + DataType = 3; + DataLength = 3; + Name = "Font style"; + } + + public override void WriteExtraToStream(Stream stream) + { + stream.WriteByte((byte)FontStyle); + stream.WriteByte((byte)FontOutlinePaletteId); + stream.WriteByte((byte)FontOutlineThickness); + } + } + + public class SubtitleRegionContentChangeFontSize : SubtitleRegionContent + { + public int FontSize { get; set; } + + public SubtitleRegionContentChangeFontSize() + { + EscapeCode = 27; + DataType = 4; + DataLength = 1; + Name = "Font size"; + } + public override void WriteExtraToStream(Stream stream) + { + stream.WriteByte((byte)FontSize); + } + } + + public class SubtitleRegionContentChangeFontColor : SubtitleRegionContent + { + public int FontPaletteId { get; set; } + + public SubtitleRegionContentChangeFontColor() + { + EscapeCode = 27; + DataType = 5; + DataLength = 1; + Name = "Font color"; + } + public override void WriteExtraToStream(Stream stream) + { + stream.WriteByte((byte)FontPaletteId); + } + } + + public class SubtitleRegionContentLineBreak : SubtitleRegionContent + { + public SubtitleRegionContentLineBreak() + { + EscapeCode = 27; + DataType = 0x0a; + Name = "Line break"; + } + public override void WriteExtraToStream(Stream stream) + { + } + } + + public class SubtitleRegionContentEndOfInlineStyle : SubtitleRegionContent + { + public SubtitleRegionContentEndOfInlineStyle() + { + EscapeCode = 27; + DataType = 0x0b; + Name = "End of inline style"; + } + public override void WriteExtraToStream(Stream stream) + { + } + } + + public class SubtitleRegion + { + public bool ContinuousPresentation { get; set; } + public bool Forced { get; set; } + public int RegionStyleId { get; set; } + public List Texts { get; set; } + public List Content { get; set; } + } + + public class DialogPresentationSegment + { + public int Length { get; set; } + public UInt64 StartPts { get; set; } + public UInt64 EndPts { get; set; } + public bool PaletteUpdate { get; set; } + public List PaletteUpdates { get; set; } + public List Regions { get; set; } + + public DialogPresentationSegment(Paragraph paragraph, RegionStyle regionStyle) + { + StartPts = (ulong)Math.Round(paragraph.StartTime.TotalMilliseconds * 90.0); + EndPts = (ulong)Math.Round(paragraph.EndTime.TotalMilliseconds * 90.0); + PaletteUpdates = new List(); + Regions = new List + { + new SubtitleRegion + { + ContinuousPresentation = false, + Forced = false, + RegionStyleId = regionStyle.RegionStyleId, + Texts = new List(), + Content = new List() + } + }; + + var content = Regions[0].Content; + var lines = paragraph.Text.SplitToLines(); + var sb = new StringBuilder(); + bool italic = false; + bool bold = false; + for (int lineNumber = 0; lineNumber < lines.Length; lineNumber++) + { + string line = lines[lineNumber]; + if (lineNumber > 0) + { + if (italic || bold) + { + content.Add(new SubtitleRegionContentEndOfInlineStyle()); + } + content.Add(new SubtitleRegionContentLineBreak()); + if (italic && bold) + { + content.Add(new SubtitleRegionContentChangeFontStyle + { + FontStyle = 3, // bold and italic + FontOutlinePaletteId = regionStyle.FontOutlinePaletteEntryIdRef, + FontOutlineThickness = regionStyle.FontOutlineThickness + }); + } + else if (italic) + { + content.Add(new SubtitleRegionContentChangeFontStyle + { + FontStyle = 2, // italic + FontOutlinePaletteId = regionStyle.FontOutlinePaletteEntryIdRef, + FontOutlineThickness = regionStyle.FontOutlineThickness + }); + } + else if (bold) + { + content.Add(new SubtitleRegionContentChangeFontStyle + { + FontStyle = 1, // bold + FontOutlinePaletteId = regionStyle.FontOutlinePaletteEntryIdRef, + FontOutlineThickness = regionStyle.FontOutlineThickness + }); + } + } + int i = 0; + while (i < line.Length) + { + string s = line.Substring(i); + if (s.StartsWith("", StringComparison.OrdinalIgnoreCase)) + { + italic = true; + if (content.Count > 0 && content[content.Count - 1] is SubtitleRegionContentChangeFontStyle) + { + content.RemoveAt(content.Count - 1); // Remove last style tag (italic/bold will be combined) + } + content.Add(new SubtitleRegionContentChangeFontStyle + { + FontStyle = bold ? 3 : 2, // italic + FontOutlinePaletteId = regionStyle.FontOutlinePaletteEntryIdRef, + FontOutlineThickness = regionStyle.FontOutlineThickness + }); + i += 3; + } + else if (s.StartsWith("", StringComparison.OrdinalIgnoreCase)) + { + italic = false; + AddText(sb, content); + if (content.Count > 0 && content[content.Count - 1] is SubtitleRegionContentEndOfInlineStyle) + { + content.RemoveAt(content.Count - 1); // Remove last to avoid duplicated + } + content.Add(new SubtitleRegionContentEndOfInlineStyle()); + i += 4; + } + else if (s.StartsWith("", StringComparison.OrdinalIgnoreCase)) + { + bold = true; + if (content.Count > 0 && content[content.Count - 1] is SubtitleRegionContentChangeFontStyle) + { + content.RemoveAt(content.Count - 1); // Remove last style tag (italic/bold will be combined) + } + content.Add(new SubtitleRegionContentChangeFontStyle + { + FontStyle = italic ? 3 : 1, // bold + FontOutlinePaletteId = regionStyle.FontOutlinePaletteEntryIdRef, + FontOutlineThickness = regionStyle.FontOutlineThickness + }); + i += 3; + } + else if (s.StartsWith("", StringComparison.OrdinalIgnoreCase)) + { + bold = false; + AddText(sb, content); + if (content.Count > 0 && content[content.Count - 1] is SubtitleRegionContentEndOfInlineStyle) + { + content.RemoveAt(content.Count - 1); // Remove last to avoid duplicated + } + content.Add(new SubtitleRegionContentEndOfInlineStyle()); + i += 4; + } + else + { + i++; + sb.Append(s.Substring(0, 1)); + } + } + AddText(sb, content); + } + if (content.Count > 0 && content[content.Count - 1] is SubtitleRegionContentEndOfInlineStyle) + { + content.RemoveAt(content.Count - 1); // last 'end-of-inline-style' not needed + } + } + + private static void AddText(StringBuilder sb, List content) + { + if (sb.Length > 0) + { + string text = HtmlUtil.RemoveHtmlTags(sb.ToString(), true); + content.Add(new SubtitleRegionContentText + { + Text = text, + DataLength = Encoding.UTF8.GetBytes(text).Length + }); + sb.Clear(); + } + } + + public DialogPresentationSegment(byte[] buffer) + { + StartPts = buffer[13]; + StartPts += (ulong)buffer[12] << 8; + StartPts += (ulong)buffer[11] << 16; + StartPts += (ulong)buffer[10] << 24; + StartPts += (ulong)(buffer[9] & Helper.B00000001) << 32; + + EndPts = buffer[18]; + EndPts += (ulong)buffer[17] << 8; + EndPts += (ulong)buffer[16] << 16; + EndPts += (ulong)buffer[15] << 24; + EndPts += (ulong)(buffer[14] & Helper.B00000001) << 32; + + PaletteUpdate = (buffer[19] & Helper.B10000000) > 0; + int idx = 20; + PaletteUpdates = new List(); + if (PaletteUpdate) + { + int numberOfPaletteEntries = buffer[21] + (buffer[20] << 8); + for (int i = 0; i < numberOfPaletteEntries; i++) + { + PaletteUpdates.Add(new Palette + { + PaletteEntryId = buffer[idx++], + Y = buffer[idx++], + Cr = buffer[idx++], + Cb = buffer[idx++], + T = buffer[idx++] + }); + } + } + + int numberOfRegions = buffer[idx++]; + Regions = new List(numberOfRegions); + for (int i = 0; i < numberOfRegions; i++) + { + var region = new SubtitleRegion { ContinuousPresentation = (buffer[idx] & Helper.B10000000) > 0, Forced = (buffer[idx] & Helper.B01000000) > 0 }; + idx++; + region.RegionStyleId = buffer[idx++]; + int regionSubtitleLength = buffer[idx + 1] + (buffer[idx] << 8); + idx += 2; + int processedLength = 0; + region.Texts = new List(); + region.Content = new List(); + string endStyle = string.Empty; + while (processedLength < regionSubtitleLength) + { + byte escapeCode = buffer[idx++]; + byte dataType = buffer[idx++]; + byte dataLength = buffer[idx++]; + processedLength += 3; + if (dataType == 0x01) // Text + { + string text = Encoding.UTF8.GetString(buffer, idx, dataLength); + region.Texts.Add(text); + region.Content.Add(new SubtitleRegionContentText + { + EscapeCode = escapeCode, + DataType = dataType, + DataLength = dataLength, + Text = text + }); + } + else if (dataType == 0x02) // Change a font set + { + region.Content.Add(new SubtitleRegionContentChangeFontSet + { + EscapeCode = escapeCode, + DataType = dataType, + DataLength = dataLength, + FontId = buffer[idx] + }); + } + else if (dataType == 0x03) // Change a font style + { + var fontStyle = buffer[idx]; + var fontOutlinePaletteId = buffer[idx + 1]; + var fontOutlineThickness = buffer[idx + 2]; + switch (fontStyle) + { + case 1: region.Texts.Add(""); + endStyle = ""; + break; + case 2: region.Texts.Add(""); + endStyle = ""; + break; + case 3: region.Texts.Add(""); + endStyle = ""; + break; + case 5: region.Texts.Add(""); + endStyle = ""; + break; + case 6: region.Texts.Add(""); + endStyle = ""; + break; + case 7: region.Texts.Add(""); + endStyle = ""; + break; + } + region.Content.Add(new SubtitleRegionContentChangeFontStyle + { + EscapeCode = escapeCode, + DataType = dataType, + DataLength = dataLength, + FontStyle = fontStyle, + FontOutlinePaletteId = fontOutlinePaletteId, + FontOutlineThickness = fontOutlineThickness + }); + } + else if (dataType == 0x04) // Change a font size + { + region.Content.Add(new SubtitleRegionContentChangeFontSize + { + EscapeCode = escapeCode, + DataType = dataType, + DataLength = dataLength, + FontSize = buffer[idx] + }); + } + else if (dataType == 0x05) // Change a font color + { + region.Content.Add(new SubtitleRegionContentChangeFontColor + { + EscapeCode = escapeCode, + DataType = dataType, + DataLength = dataLength, + FontPaletteId = buffer[idx] + }); + } + else if (dataType == 0x0A) // Line break + { + region.Texts.Add(Environment.NewLine); + region.Content.Add(new SubtitleRegionContentLineBreak + { + EscapeCode = escapeCode, + DataType = dataType, + DataLength = dataLength, + }); + } + else if (dataType == 0x0B) // End of inline style + { + if (!string.IsNullOrEmpty(endStyle)) + { + region.Texts.Add(endStyle); + endStyle = string.Empty; + } + region.Content.Add(new SubtitleRegionContentEndOfInlineStyle + { + EscapeCode = escapeCode, + DataType = dataType, + DataLength = dataLength, + }); + } + processedLength += dataLength; + idx += dataLength; + } + if (!string.IsNullOrEmpty(endStyle)) + { + region.Texts.Add(endStyle); + } + Regions.Add(region); + } + } + + public string Text + { + get + { + var sb = new StringBuilder(); + foreach (var region in Regions) + { + foreach (string text in region.Texts) + { + sb.Append(text); + } + } + return sb.ToString(); + } + } + + public ulong StartPtsMilliseconds + { + get { return (ulong)Math.Round((StartPts) / 90.0); } + } + + public ulong EndPtsMilliseconds + { + get { return (ulong)Math.Round((EndPts) / 90.0); } + } + + public void WriteToStream(Stream stream) + { + byte[] regionSubtitle = MakeSubtitleRegions(); + stream.Write(new byte[] { 0, 0, 1, 0xbf }, 0, 4); // MPEG-2 Private stream 2 + int size = regionSubtitle.Length + 15; + stream.WriteWord(size); + stream.WriteByte(SegmentTypeDialogPresentation); // 0x82 + stream.WriteWord(size - 3); + stream.WritePts(StartPts); + stream.WritePts(EndPts); + if (PaletteUpdate) + { + stream.WriteWord(PaletteUpdates.Count); + foreach (var palette in PaletteUpdates) + { + stream.WriteByte((byte)palette.PaletteEntryId); + stream.WriteByte((byte)palette.Y); + stream.WriteByte((byte)palette.Cb); + stream.WriteByte((byte)palette.Cr); + stream.WriteByte((byte)palette.T); + } + } + else + { + stream.WriteByte(0); // 1 bit = palette update (0=no update), next 7 bits reserved + } + stream.WriteByte((byte)Regions.Count); // number of regions + + stream.Write(regionSubtitle, 0, regionSubtitle.Length); + } + + private byte[] MakeSubtitleRegions() + { + using (var ms = new MemoryStream()) + { + foreach (var subtitleRegion in Regions) + { + byte flags = 0; + if (subtitleRegion.ContinuousPresentation) + flags = (byte)(flags | Helper.B10000000); + if (subtitleRegion.Forced) + flags = (byte)(flags | Helper.B01000000); + ms.WriteByte(flags); // first byte=continuous_present_flag, second byte=force, next 6 bits reserved + + ms.WriteByte((byte)subtitleRegion.RegionStyleId); + var contentBuffer = MakeSubtitleRegionContent(subtitleRegion); + ms.WriteWord(contentBuffer.Length); // set region subtitle size field + ms.Write(contentBuffer, 0, contentBuffer.Length); + } + return ms.ToArray(); + } + } + + private static byte[] MakeSubtitleRegionContent(SubtitleRegion subtitleRegion) + { + using (var ms = new MemoryStream()) + { + foreach (var content in subtitleRegion.Content) + { + ms.WriteByte((byte)content.EscapeCode); // escape code (0x1b / 27) + ms.WriteByte((byte)content.DataType); + ms.WriteByte((byte)content.DataLength); + content.WriteExtraToStream(ms); + } + return ms.ToArray(); + } + } + } + + public DialogStyleSegment StyleSegment; + public List PresentationSegments; + + private const int TextSubtitleStreamPid = 0x1800; + private const byte SegmentTypeDialogStyle = 0x81; + private const byte SegmentTypeDialogPresentation = 0x82; + + public override string Extension + { + get { return ".m2ts"; } + } + + public override string Name + { + get { return "Blu-ray TextST"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + if (string.IsNullOrEmpty(fileName)) + return false; + + if ((fileName.EndsWith(".m2ts", StringComparison.OrdinalIgnoreCase) && FileUtil.IsM2TransportStream(fileName)) || + (fileName.EndsWith(".textst", StringComparison.OrdinalIgnoreCase) && FileUtil.IsMpeg2PrivateStream2(fileName))) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > 0; + } + return false; + } + + public override string ToText(Subtitle subtitle, string title) + { + throw new NotImplementedException(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + if (FileUtil.IsMpeg2PrivateStream2(fileName)) + { + using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + LoadSubtitleFromMpeg2PesPackets(subtitle, fs); + } + } + else + { + using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + LoadSubtitleFromM2Ts(subtitle, fs); + } + } + subtitle.Renumber(); + } + + private void LoadSubtitleFromMpeg2PesPackets(Subtitle subtitle, Stream stream) + { + long position = 0; + stream.Position = 0; + stream.Seek(position, SeekOrigin.Begin); + long streamLength = stream.Length; + var buffer = new byte[512]; + PresentationSegments = new List(); + while (position < streamLength) + { + stream.Seek(position, SeekOrigin.Begin); + int bytesRead = stream.Read(buffer, 0, buffer.Length); + if (bytesRead < 20) + break; + + int size = (buffer[4] << 8) + buffer[5] + 6; + position += size; + + if (bytesRead > 10 && VobSubParser.IsPrivateStream2(buffer, 0)) + { + if (buffer[6] == SegmentTypeDialogPresentation) + { + var dps = new DialogPresentationSegment(buffer); + PresentationSegments.Add(dps); + subtitle.Paragraphs.Add(new Paragraph(dps.Text.Trim(), dps.StartPtsMilliseconds, dps.EndPtsMilliseconds)); + } + else if (buffer[6] == SegmentTypeDialogStyle) + { + StyleSegment = new DialogStyleSegment(buffer); + } + } + } + } + + private void LoadSubtitleFromM2Ts(Subtitle subtitle, Stream ms) + { + var subtitlePackets = new List(); + const int packetLength = 188; + bool isM2TransportStream = DetectFormat(ms); + var packetBuffer = new byte[packetLength]; + var m2TsTimeCodeBuffer = new byte[4]; + long position = 0; + ms.Position = 0; + + // check for Topfield .rec file + ms.Seek(position, SeekOrigin.Begin); + ms.Read(m2TsTimeCodeBuffer, 0, 3); + if (m2TsTimeCodeBuffer[0] == 0x54 && m2TsTimeCodeBuffer[1] == 0x46 && m2TsTimeCodeBuffer[2] == 0x72) + position = 3760; + + long transportStreamLength = ms.Length; + while (position < transportStreamLength) + { + ms.Seek(position, SeekOrigin.Begin); + if (isM2TransportStream) + { + ms.Read(m2TsTimeCodeBuffer, 0, m2TsTimeCodeBuffer.Length); + var tc = (m2TsTimeCodeBuffer[0] << 24) + (m2TsTimeCodeBuffer[1] << 16) + (m2TsTimeCodeBuffer[2] << 8) + (m2TsTimeCodeBuffer[3] & Helper.B00111111); + // should m2ts time code be used in any way? + var msecs = (ulong)Math.Round((tc) / 27.0); // 27 or 90? + var tc2 = new TimeCode(msecs); + System.Diagnostics.Debug.WriteLine(tc2); + position += m2TsTimeCodeBuffer.Length; + } + + ms.Read(packetBuffer, 0, packetLength); + byte syncByte = packetBuffer[0]; + if (syncByte == Packet.SynchronizationByte) + { + var packet = new Packet(packetBuffer); + if (packet.PacketId == TextSubtitleStreamPid) + { + subtitlePackets.Add(packet); + } + position += packetLength; + } + else + { + position++; + } + } + + //TODO: merge ts packets + + PresentationSegments = new List(); + foreach (var item in subtitlePackets) + { + if (item.Payload != null && item.Payload.Length > 10 && VobSubParser.IsPrivateStream2(item.Payload, 0)) + { + if (item.Payload[6] == SegmentTypeDialogPresentation) + { + var dps = new DialogPresentationSegment(item.Payload); + PresentationSegments.Add(dps); + subtitle.Paragraphs.Add(new Paragraph(dps.Text.Trim(), dps.StartPtsMilliseconds, dps.EndPtsMilliseconds)); + } + else if (item.Payload[6] == SegmentTypeDialogStyle) + { + StyleSegment = new DialogStyleSegment(item.Payload); + } + } + } + + subtitle.Renumber(); + } + + private bool DetectFormat(Stream ms) + { + if (ms.Length > 192 + 192 + 5) + { + ms.Seek(0, SeekOrigin.Begin); + var buffer = new byte[192 + 192 + 5]; + ms.Read(buffer, 0, buffer.Length); + if (buffer[0] == Packet.SynchronizationByte && buffer[188] == Packet.SynchronizationByte) + return false; + if (buffer[4] == Packet.SynchronizationByte && buffer[192 + 4] == Packet.SynchronizationByte && buffer[192 + 192 + 4] == Packet.SynchronizationByte) + { + return true; + } + } + return false; + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/TimeXml.cs b/libse/SubtitleFormats/TimeXml.cs new file mode 100644 index 000000000..191d19d80 --- /dev/null +++ b/libse/SubtitleFormats/TimeXml.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class TimeXml : SubtitleFormat + { + public override string Extension + { + get { return ".xml"; } + } + + public override string Name + { + get { return "Xml"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > 0; + } + + public override string ToText(Subtitle subtitle, string title) + { + string xmlStructure = + "" + Environment.NewLine + + ""; + + var xml = new XmlDocument(); + xml.LoadXml(xmlStructure); + + foreach (Paragraph p in subtitle.Paragraphs) + { + XmlNode paragraph = xml.CreateElement("Paragraph"); + + XmlNode number = xml.CreateElement("Number"); + number.InnerText = p.Number.ToString(CultureInfo.InvariantCulture); + paragraph.AppendChild(number); + + XmlNode start = xml.CreateElement("StartMilliseconds"); + start.InnerText = ((long)(Math.Round(p.StartTime.TotalMilliseconds))).ToString(CultureInfo.InvariantCulture); + paragraph.AppendChild(start); + + XmlNode end = xml.CreateElement("EndMilliseconds"); + end.InnerText = ((long)(Math.Round(p.EndTime.TotalMilliseconds))).ToString(CultureInfo.InvariantCulture); + paragraph.AppendChild(end); + + XmlNode text = xml.CreateElement("Text"); + text.InnerText = HtmlUtil.RemoveHtmlTags(p.Text); + paragraph.AppendChild(text); + + xml.DocumentElement.AppendChild(paragraph); + } + + return ToUtf8XmlString(xml); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + + string xmlString = sb.ToString(); + if (!xmlString.Contains("") || !xmlString.Contains("")) + return; + + var xml = new XmlDocument { XmlResolver = null }; + try + { + xml.LoadXml(xmlString); + } + catch + { + _errorCount = 1; + return; + } + + foreach (XmlNode node in xml.DocumentElement.SelectNodes("Paragraph")) + { + try + { + string start = node.SelectSingleNode("StartMilliseconds").InnerText; + string end = node.SelectSingleNode("EndMilliseconds").InnerText; + string text = node.SelectSingleNode("Text").InnerText; + + subtitle.Paragraphs.Add(new Paragraph(text, Convert.ToDouble(start, CultureInfo.InvariantCulture), Convert.ToDouble(end, CultureInfo.InvariantCulture))); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + _errorCount++; + } + } + subtitle.Renumber(); + } + + } +} diff --git a/libse/SubtitleFormats/TimeXml2.cs b/libse/SubtitleFormats/TimeXml2.cs new file mode 100644 index 000000000..d38e188de --- /dev/null +++ b/libse/SubtitleFormats/TimeXml2.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class TimeXml2 : SubtitleFormat + { + public override string Extension + { + get { return ".xml"; } + } + + public override string Name + { + get { return "Xml 2"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > 0; + } + + public override string ToText(Subtitle subtitle, string title) + { + string xmlStructure = + "" + Environment.NewLine + + ""; + + var xml = new XmlDocument(); + xml.LoadXml(xmlStructure); + + foreach (Paragraph p in subtitle.Paragraphs) + { + XmlNode paragraph = xml.CreateElement("Subtitle"); + + XmlNode number = xml.CreateElement("Number"); + number.InnerText = p.Number.ToString(CultureInfo.InvariantCulture); + paragraph.AppendChild(number); + + XmlNode start = xml.CreateElement("Start"); + start.InnerText = p.StartTime.ToString(); + paragraph.AppendChild(start); + + XmlNode end = xml.CreateElement("End"); + end.InnerText = p.EndTime.ToString(); + paragraph.AppendChild(end); + + XmlNode duration = xml.CreateElement("Duration"); + duration.InnerText = p.Duration.ToShortString(); + paragraph.AppendChild(duration); + + XmlNode text = xml.CreateElement("Text"); + text.InnerText = HtmlUtil.RemoveHtmlTags(p.Text); + paragraph.AppendChild(text); + + xml.DocumentElement.AppendChild(paragraph); + } + + return ToUtf8XmlString(xml); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + + var xmlString = sb.ToString(); + if (!xmlString.Contains("") || !xmlString.Contains("") || !xmlString.Contains("")) + return; + + var xml = new XmlDocument { XmlResolver = null }; + try + { + xml.LoadXml(xmlString); + } + catch + { + _errorCount = 1; + return; + } + + foreach (XmlNode node in xml.DocumentElement.SelectNodes("Subtitle")) + { + try + { + TimeCode startTimeCode = DecodeTimeCode(node.SelectSingleNode("Start").InnerText); + TimeCode endTimeCode = DecodeTimeCode(node.SelectSingleNode("End").InnerText); + string text = node.SelectSingleNode("Text").InnerText; + subtitle.Paragraphs.Add(new Paragraph(startTimeCode, endTimeCode, text)); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + _errorCount++; + } + } + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string p) + { + var parts = p.Split(new[] { ';', '.', ':', ',' }); + + string hour = parts[0]; + string minutes = parts[1]; + string seconds = parts[2]; + string ms = parts[3]; + + TimeCode tc = new TimeCode(int.Parse(hour), int.Parse(minutes), int.Parse(seconds), (int.Parse(ms))); + return tc; + } + + } +} diff --git a/libse/SubtitleFormats/TimedText.cs b/libse/SubtitleFormats/TimedText.cs new file mode 100644 index 000000000..cf131e137 --- /dev/null +++ b/libse/SubtitleFormats/TimedText.cs @@ -0,0 +1,291 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class TimedText : SubtitleFormat + { + public override string Extension + { + get { return ".xml"; } + } + + public override string Name + { + get { return "Timed Text draft 2006-10"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + string xmlAsString = sb.ToString().Trim(); + + if (xmlAsString.Contains("xmlns:tts=\"http://www.w3.org/2006/04")) + return false; + + if (xmlAsString.Contains("http://www.w3.org/") && + xmlAsString.Contains("/ttaf1")) + { + var xml = new XmlDocument { XmlResolver = null }; + try + { + xml.LoadXml(xmlAsString); + + var nsmgr = new XmlNamespaceManager(xml.NameTable); + nsmgr.AddNamespace("ttaf1", xml.DocumentElement.NamespaceURI); + XmlNode div = xml.DocumentElement.SelectSingleNode("//ttaf1:body", nsmgr).SelectSingleNode("ttaf1:div", nsmgr); + if (div == null) + div = xml.DocumentElement.SelectSingleNode("//ttaf1:body", nsmgr).FirstChild; + int numberOfParagraphs = div.ChildNodes.Count; + return numberOfParagraphs > 0; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + return false; + } + } + return false; + } + + private static string ConvertToTimeString(TimeCode time) + { + return string.Format("{0:00}:{1:00}:{2:00}.{3:000}", time.Hours, time.Minutes, time.Seconds, time.Milliseconds); + } + + public override string ToText(Subtitle subtitle, string title) + { + string xmlStructure = + "" + Environment.NewLine + + "" + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + " + + + + +"; + xmlStructure = xmlStructure.Replace("[DATE]", DateTime.Now.ToString("yyyy-MM-dd")); + + var xml = new XmlDocument(); + xml.LoadXml(xmlStructure); + xml.DocumentElement.SelectSingleNode("metadata/title").InnerText = title; + var subtitlesNode = xml.DocumentElement.SelectSingleNode("subtitles"); + + foreach (Paragraph p in subtitle.Paragraphs) + { + XmlNode paragraph = xml.CreateElement("subtitle"); + + XmlAttribute start = xml.CreateAttribute("start"); + start.InnerText = p.StartTime.ToString().Replace(",", "."); + paragraph.Attributes.Prepend(start); + + XmlAttribute stop = xml.CreateAttribute("stop"); + stop.InnerText = p.EndTime.ToString().Replace(",", "."); + paragraph.Attributes.Append(stop); + + XmlNode text = xml.CreateElement("text"); + text.InnerText = HtmlUtil.RemoveHtmlTags(p.Text); + paragraph.AppendChild(text); + + XmlAttribute style = xml.CreateAttribute("style"); + style.InnerText = "Default"; + text.Attributes.Append(style); + + subtitlesNode.AppendChild(paragraph); + } + + return ToUtf8XmlString(xml); + } + + private static TimeCode DecodeTimeCode(string code) + { + string[] parts = code.Split(new[] { ':', '.', ',' }, StringSplitOptions.RemoveEmptyEntries); + + //00:00:07:12 + string hour = parts[0]; + string minutes = parts[1]; + string seconds = parts[2]; + string frames = parts[3]; + + return new TimeCode(int.Parse(hour), int.Parse(minutes), int.Parse(seconds), FramesToMillisecondsMax999(int.Parse(frames))); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + + string xmlString = sb.ToString(); + if (!xmlString.Contains("")) + return; + + var xml = new XmlDocument(); + xml.XmlResolver = null; + try + { + xml.LoadXml(xmlString); + } + catch + { + _errorCount = 1; + return; + } + + foreach (XmlNode node in xml.DocumentElement.SelectNodes("subtitles/subtitle")) + { + try + { + string start = node.Attributes["start"].InnerText; + string stop = node.Attributes["stop"].InnerText; + string text = node.SelectSingleNode("text").InnerText; + + subtitle.Paragraphs.Add(new Paragraph(DecodeTimeCode(start), DecodeTimeCode(stop), text)); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + _errorCount++; + } + } + subtitle.Renumber(); + } + + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle1.cs b/libse/SubtitleFormats/UnknownSubtitle1.cs new file mode 100644 index 000000000..22fe1d8ba --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle1.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle1 : SubtitleFormat + { + //0:01 – 0:11 + private static readonly Regex RegexTimeCodes = new Regex(@"^\d+:\d\d – \d+:\d\d ", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 1"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + //0:01 – 0:11 "My vengeance needs blood!" -Marquis De Sade + //[Laughter, thunder] + //0:17 – 0:19 - On this 5th day of December - + //0:19 – 0:22 in the year of our Lord 1648, - + + const string paragraphWriteFormat = "{0} – {1} {2}"; + + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + int seconds = p.StartTime.Seconds; + if (p.StartTime.Milliseconds >= 500) + seconds++; + string startTime = string.Format("{0:0}:{1:00}", (int)(p.StartTime.Minutes + p.StartTime.Hours * 60), seconds); + + seconds = p.EndTime.Seconds; + if (p.EndTime.Milliseconds >= 500) + seconds++; + string timeOut = string.Format("{0:0}:{1:00}", (int)(p.EndTime.Minutes + p.EndTime.Hours * 60), seconds); + + sb.AppendLine(string.Format(paragraphWriteFormat, startTime, timeOut, p.Text)); + } + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + Paragraph p = null; + _errorCount = 0; + + subtitle.Paragraphs.Clear(); + var text = new StringBuilder(); + + foreach (string line in lines) + { + var match = RegexTimeCodes.Match(line); + if (match.Success) + { + if (p != null) + p.Text = (p.Text + Environment.NewLine + text.ToString().Trim()).Trim(); + var parts = line.Substring(0, match.Length).Trim().Split(new[] { '–', ' ' }, StringSplitOptions.RemoveEmptyEntries); + try + { + p = new Paragraph(); + p.StartTime = DecodeTimeCode(parts[0]); + p.EndTime = DecodeTimeCode(parts[1]); + p.Text = line.Substring(match.Length - 1).Trim(); + subtitle.Paragraphs.Add(p); + text = new StringBuilder(); + } + catch + { + p = null; + _errorCount++; + } + } + else if (p == null) + { + _errorCount++; + } + else + { + text.AppendLine(line); + } + if (_errorCount > 20) + return; + } + if (p != null) + p.Text = (p.Text + Environment.NewLine + text.ToString().Trim()).Trim(); + + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string code) + { + //68:20 (minutes:seconds) + var parts = code.Trim().Split(':'); + return new TimeCode(0, int.Parse(parts[0]), int.Parse(parts[1]), 0); + } + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/UnknownSubtitle10.cs b/libse/SubtitleFormats/UnknownSubtitle10.cs new file mode 100644 index 000000000..31635324b --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle10.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle10 : SubtitleFormat + { + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 10"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + sb.Append("{\"language_code\":\"en\",\"subtitles\":["); + int i = 0; + foreach (Paragraph p in subtitle.Paragraphs) + { + if (i > 0) + sb.Append(','); + sb.Append('{'); + sb.AppendFormat("\"content\":\"{0}\",\"start_time\":{1},\"end_time\":{2}", p.Text.Replace(Environment.NewLine, "
"), ((long)(Math.Round(p.StartTime.TotalMilliseconds))).ToString(CultureInfo.InvariantCulture), ((long)(Math.Round(p.EndTime.TotalMilliseconds))).ToString(CultureInfo.InvariantCulture)); + sb.Append('}'); + i++; + } + sb.Append("]}"); + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + var temp = new StringBuilder(); + foreach (string l in lines) + temp.Append(l); + string all = temp.ToString(); + if (!all.Contains("{\"content\":\"")) + return; + + var arr = all.Replace("\n", string.Empty).Replace("{\"content\":\"", "\n").Split('\n'); + + _errorCount = 0; + subtitle.Paragraphs.Clear(); + + // {"content":"La ce se gandeste Oh Ha Ni a noastra
de la inceputul dimineti?","start_time":314071,"end_time":317833}, + for (int i = 0; i < arr.Length; i++) + { + string line = arr[i].Trim(); + + int indexStartTime = line.IndexOf("\"start_time\":", StringComparison.Ordinal); + int indexEndTime = line.IndexOf("\"end_time\":", StringComparison.Ordinal); + if (indexStartTime > 0 && indexEndTime > 0) + { + int indexEndText = indexStartTime; + if (indexStartTime > indexEndTime) + indexEndText = indexEndTime; + string text = line.Substring(0, indexEndText - 1).Trim().TrimEnd('\"'); + text = text.Replace("
", Environment.NewLine).Replace("
", Environment.NewLine); + text = text.Replace("
", Environment.NewLine).Replace("
", Environment.NewLine); + text = text.Replace(Environment.NewLine + " ", Environment.NewLine); + text = text.Replace(Environment.NewLine + " ", Environment.NewLine); + text = text.Replace(Environment.NewLine + " ", Environment.NewLine); + text = text.Replace(" " + Environment.NewLine, Environment.NewLine); + text = text.Replace(" " + Environment.NewLine, Environment.NewLine); + text = text.Replace(" " + Environment.NewLine, Environment.NewLine); + try + { + string start = line.Substring(indexStartTime); + string end = line.Substring(indexEndTime); + var paragraph = new Paragraph + { + Text = text, + StartTime = { TotalMilliseconds = GetMilliseconds(start) }, + EndTime = { TotalMilliseconds = GetMilliseconds(end) } + }; + subtitle.Paragraphs.Add(paragraph); + } + catch (Exception exception) + { + System.Diagnostics.Debug.WriteLine(exception.Message); + _errorCount++; + } + } + } + subtitle.Renumber(); + } + + private static double GetMilliseconds(string start) + { + while (start.Length > 1 && !start.StartsWith(':')) + start = start.Remove(0, 1); + start = start.Trim().Trim(':').Trim('"').Trim(); + + int i = 0; + while (i < start.Length && char.IsDigit(start[i])) + i++; + + return int.Parse(start.Substring(0, i)); + } + + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle11.cs b/libse/SubtitleFormats/UnknownSubtitle11.cs new file mode 100644 index 000000000..b45b8e750 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle11.cs @@ -0,0 +1,272 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + /// + /// MicroDVD with time codes...? + /// + public class UnknownSubtitle11 : SubtitleFormat + { + private static readonly Regex RegexMicroDvdLine = new Regex(@"^\{-?\d+:\d+:\d+}\{-?\d+:\d+:\d+}.*$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".sub"; } + } + + public override string Name + { + get { return "Unknown 11"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var trimmedLines = new List(); + int errors = 0; + foreach (string line in lines) + { + if (line.Contains('{')) + { + string s = RemoveIllegalSpacesAndFixEmptyCodes(line); + if (RegexMicroDvdLine.IsMatch(s)) + trimmedLines.Add(s); + else + errors++; + } + else + { + errors++; + } + } + + return trimmedLines.Count > errors; + } + + private static string RemoveIllegalSpacesAndFixEmptyCodes(string line) + { + int index = line.IndexOf('}'); + if (index >= 0 && index < line.Length) + { + index = line.IndexOf('}', index + 1); + if (index >= 0 && index + 1 < line.Length) + { + var indexOfBrackets = line.IndexOf("{}", StringComparison.Ordinal); + if (indexOfBrackets >= 0 && indexOfBrackets < index) + { + line = line.Insert(indexOfBrackets + 1, "0"); // set empty time codes to zero + index++; + } + + while (line.Contains(' ') && line.IndexOf(' ') < index) + { + line = line.Remove(line.IndexOf(' '), 1); + index--; + } + } + } + return line; + } + + private static string MakeTimeCode(TimeCode tc) + { + return string.Format("{0}:{1:00}:{2:00}", tc.Hours, tc.Minutes, tc.Seconds); + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.Append('{'); + sb.Append(MakeTimeCode(p.StartTime)); + sb.Append("}{"); + sb.Append(MakeTimeCode(p.EndTime)); + sb.Append('}'); + + //{y:b} is italics for single line + //{Y:b} is italics for both lines + + var parts = p.Text.SplitToLines(); + int count = 0; + bool italicOn = false; + bool boldOn = false; + bool underlineOn = false; + StringBuilder lineSb = new StringBuilder(); + foreach (string line in parts) + { + if (count > 0) + lineSb.Append('|'); + + if (line.StartsWith("") || italicOn) + { + italicOn = true; + boldOn = false; + underlineOn = false; + lineSb.Append("{y:i}"); // italic single line + } + else if (line.StartsWith("") || boldOn) + { + italicOn = false; + boldOn = true; + underlineOn = false; + lineSb.Append("{y:b}"); // bold single line + } + else if (line.StartsWith("") || underlineOn) + { + italicOn = false; + boldOn = false; + underlineOn = true; + lineSb.Append("{y:u}"); // underline single line + } + + if (line.Contains("")) + italicOn = false; + + if (line.Contains("")) + boldOn = false; + + if (line.Contains("")) + underlineOn = false; + + lineSb.Append(HtmlUtil.RemoveHtmlTags(line)); + count++; + } + string text = lineSb.ToString(); + int noOfLines = Utilities.CountTagInText(text, '|') + 1; + if (noOfLines > 1 && Utilities.CountTagInText(text, "{y:i}") == noOfLines) + text = "{Y:i}" + text.Replace("{y:i}", string.Empty); + else if (noOfLines > 1 && Utilities.CountTagInText(text, "{y:b}") == noOfLines) + text = "{Y:b}" + text.Replace("{y:b}", string.Empty); + else if (noOfLines > 1 && Utilities.CountTagInText(text, "{y:u}") == noOfLines) + text = "{Y:u}" + text.Replace("{y:u}", string.Empty); + sb.AppendLine(HtmlUtil.RemoveHtmlTags(text)); + } + return sb.ToString().Trim(); + } + + private static TimeCode DecodeTimeCode(string timeCode) + { + string[] arr = timeCode.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + return new TimeCode(int.Parse(arr[0]), int.Parse(arr[1]), int.Parse(arr[2]), 0); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + + foreach (string line in lines) + { + string s = RemoveIllegalSpacesAndFixEmptyCodes(line); + if (RegexMicroDvdLine.IsMatch(s)) + { + try + { + int textIndex = GetTextStartIndex(s); + if (textIndex < s.Length) + { + string text = s.Substring(textIndex); + string temp = s.Substring(0, textIndex - 1); + string[] frames = temp.Replace("}{", ";").Replace("{", string.Empty).Replace("}", string.Empty).Split(';'); + + TimeCode startTime = DecodeTimeCode(frames[0]); + TimeCode endTime = DecodeTimeCode(frames[1]); + + string post = string.Empty; + string[] parts = text.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); + int count = 0; + StringBuilder lineSb = new StringBuilder(); + foreach (string s2 in parts) + { + if (count > 0) + lineSb.AppendLine(); + + s = s2.Trim(); + if (s.StartsWith("{Y:i}")) + { + s = "" + s.Replace("{Y:i}", string.Empty); + post += ""; + } + else if (s.StartsWith("{Y:b}")) + { + s = "" + s.Replace("{Y:b}", string.Empty); + post += ""; + } + else if (s.StartsWith("{Y:u}")) + { + s = "" + s.Replace("{Y:u}", string.Empty); + post += ""; + } + else if (s.StartsWith("{y:i}")) + { + s = "" + s.Replace("{y:i}", string.Empty) + ""; + } + else if (s.StartsWith("{y:b}")) + { + s = "" + s.Replace("{y:b}", string.Empty) + ""; + } + else if (s.StartsWith("{y:u}")) + { + s = "" + s.Replace("{y:u}", string.Empty) + ""; + } + s = s.Replace("{Y:i}", string.Empty).Replace("{y:i}", string.Empty); + s = s.Replace("{Y:b}", string.Empty).Replace("{y:b}", string.Empty); + s = s.Replace("{Y:u}", string.Empty).Replace("{y:u}", string.Empty); + lineSb.Append(s); + count++; + } + text = lineSb + post; + subtitle.Paragraphs.Add(new Paragraph(startTime, endTime, text)); + } + } + catch + { + _errorCount++; + } + } + else + { + _errorCount++; + } + } + + int i = 0; + foreach (Paragraph p in subtitle.Paragraphs) + { + Paragraph previous = subtitle.GetParagraphOrDefault(i - 1); + if (p.StartFrame == 0 && previous != null) + { + p.StartFrame = previous.EndFrame + 1; + } + if (p.EndFrame == 0) + { + p.EndFrame = p.StartFrame; + } + i++; + } + + subtitle.Renumber(); + } + + private static int GetTextStartIndex(string line) + { + int i = 0; + int tagCount = 0; + while (i < line.Length && tagCount < 4) + { + if (line[i] == '{' || line[i] == '}') + tagCount++; + + i++; + } + return i; + } + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/UnknownSubtitle12.cs b/libse/SubtitleFormats/UnknownSubtitle12.cs new file mode 100644 index 000000000..cb105f69c --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle12.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + /// + ///4.01 5.12 + ///Dit is de dag. + /// + public class UnknownSubtitle12 : SubtitleFormat + { + private static readonly Regex RegexTimeCode = new Regex(@"^\d+.\d\d\t\t\d+.\d\d\t*$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 12"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + private static string MakeTimeCode(TimeCode tc) + { + return string.Format("{0:0.00}", tc.TotalSeconds); + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.Append(MakeTimeCode(p.StartTime)); + sb.Append("\t\t"); + sb.Append(MakeTimeCode(p.EndTime)); + sb.Append("\t\t\n"); + sb.Append(p.Text.Replace(Environment.NewLine, "\n") + "\n\n"); + } + return sb.ToString().Trim(); + } + + private static TimeCode DecodeTimeCode(string timeCode) + { + return TimeCode.FromSeconds(double.Parse(timeCode.Trim())); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + Paragraph p = null; + var text = new StringBuilder(); + foreach (string line in lines) + { + string s = line.Trim(); + if (RegexTimeCode.IsMatch(s)) + { + try + { + if (p != null) + { + p.Text = text.ToString().Trim(); + subtitle.Paragraphs.Add(p); + } + string[] arr = s.Split(new[] { '\t' }, StringSplitOptions.RemoveEmptyEntries); + text = new StringBuilder(); + p = new Paragraph(DecodeTimeCode(arr[0]), DecodeTimeCode(arr[1]), ""); + } + catch + { + _errorCount++; + p = null; + } + } + else if (p != null) + { + text.AppendLine(s); + } + else + { + _errorCount++; + } + } + if (p != null && text.Length > 0) + { + p.Text = text.ToString().Trim(); + subtitle.Paragraphs.Add(p); + } + subtitle.Renumber(); + } + + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle13.cs b/libse/SubtitleFormats/UnknownSubtitle13.cs new file mode 100644 index 000000000..306d04382 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle13.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle13 : SubtitleFormat + { + public override string Extension + { + get { return ".xml"; } + } + + public override string Name + { + get { return "Unknown 13"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > 0; + } + + public override string ToText(Subtitle subtitle, string title) + { + string xmlStructure = + "" + Environment.NewLine + + ""; + + var xml = new XmlDocument(); + xml.LoadXml(xmlStructure); + + int id = 1; + foreach (Paragraph p in subtitle.Paragraphs) + { + XmlNode paragraph = xml.CreateElement("entry"); + + XmlAttribute duration = xml.CreateAttribute("timeOut"); + duration.InnerText = p.EndTime.ToString(); + paragraph.Attributes.Append(duration); + + XmlAttribute start = xml.CreateAttribute("timeIn"); + start.InnerText = p.StartTime.ToString(); + paragraph.Attributes.Append(start); + + XmlAttribute idAttr = xml.CreateAttribute("id"); + idAttr.InnerText = id.ToString(CultureInfo.InvariantCulture); + paragraph.Attributes.Append(idAttr); + + paragraph.InnerText = " lines, string fileName) + { + _errorCount = 0; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + + string allText = sb.ToString(); + if (!allText.Contains("") || !allText.Contains("timeIn=")) + return; + + var xml = new XmlDocument { XmlResolver = null }; + try + { + xml.LoadXml(allText); + } + catch (Exception exception) + { + System.Diagnostics.Debug.WriteLine(exception.Message); + _errorCount = 1; + return; + } + + foreach (XmlNode node in xml.DocumentElement.SelectNodes("entry")) + { + try + { + string start = node.Attributes["timeIn"].InnerText; + string end = node.Attributes["timeOut"].InnerText; + string text = node.InnerText; + if (text.StartsWith("![CDATA[", StringComparison.Ordinal)) + text = text.Remove(0, 8); + if (text.StartsWith(" lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > 0; + } + + public override string ToText(Subtitle subtitle, string title) + { + // + // XYZ PRESENTS + // + + string xmlStructure = + "" + Environment.NewLine + + ""; + + var xml = new XmlDocument(); + xml.LoadXml(xmlStructure); + + foreach (Paragraph p in subtitle.Paragraphs) + { + XmlNode paragraph = xml.CreateElement("Phrase"); + + XmlAttribute start = xml.CreateAttribute("TimeStart"); + start.InnerText = ((long)(Math.Round(p.StartTime.TotalMilliseconds))).ToString(CultureInfo.InvariantCulture); + paragraph.Attributes.Append(start); + + XmlAttribute end = xml.CreateAttribute("TimeEnd"); + end.InnerText = ((long)(Math.Round(p.EndTime.TotalMilliseconds))).ToString(CultureInfo.InvariantCulture); + paragraph.Attributes.Append(end); + + XmlNode text = xml.CreateElement("Text"); + text.InnerText = HtmlUtil.RemoveHtmlTags(p.Text).Replace(Environment.NewLine, "\\n"); + paragraph.AppendChild(text); + + xml.DocumentElement.AppendChild(paragraph); + } + + return ToUtf8XmlString(xml); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + + string allText = sb.ToString(); + if (!allText.Contains(" lines, string fileName) + { + var subtitle = new Subtitle(); + this.LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > 0; + } + + private static string ToTimeCode(TimeCode tc) + { + int last = (int)(tc.Milliseconds / 10.0D + 0.5D); + return tc.ToString().Substring(0, 8) + ":" + string.Format("{0:0#}", last); + } + + private static TimeCode DecodeTimeCode(string s) + { + var parts = s.Split(new[] { ';', ':' }, StringSplitOptions.RemoveEmptyEntries); + var tc = new TimeCode(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), int.Parse(parts[3]) * 100); + return tc; + } + + public override string ToText(Subtitle subtitle, string title) + { + // + //
"); + else + s = s.Remove(index, 1).Insert(index, ""); + italicOn = !italicOn; + } + + // force title + s = s.Replace("@/", string.Empty); + + paragraph.Text = (paragraph.Text + Environment.NewLine + s).Trim(); + if (paragraph.Text.Length > 2000) + { + _errorCount += 100; + return; + } + } + } + if (paragraph != null && !string.IsNullOrEmpty(paragraph.Text)) + subtitle.Paragraphs.Add(paragraph); + + foreach (Paragraph p in subtitle.Paragraphs) + { + //@+: reposition top + //@|: reposition middle + if (p.Text.Contains("@+")) + p.Text = "{\\an8}" + p.Text.Replace("@+", string.Empty).Replace("@|", string.Empty); + else if (p.Text.Contains("@|")) + p.Text = "{\\an5}" + p.Text.Replace("@+", string.Empty).Replace("@|", string.Empty); + } + + subtitle.Renumber(); + } + + private static string EncodeTimeCode(TimeCode time) + { + //00:03:15.22 (last is frame) + return string.Format("{0:00}:{1:00}:{2:00}.{3:00}", time.Hours, time.Minutes, time.Seconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); + } + + private static TimeCode DecodeTimeCode(string timeCode) + { + string[] arr = timeCode.Split(new[] { ':', ';', ',', '.' }, StringSplitOptions.RemoveEmptyEntries); + return new TimeCode(int.Parse(arr[0]), int.Parse(arr[1]), int.Parse(arr[2]), FramesToMillisecondsMax999(int.Parse(arr[3]))); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/UnknownSubtitle33.cs b/libse/SubtitleFormats/UnknownSubtitle33.cs new file mode 100644 index 000000000..25ee85d95 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle33.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle33 : SubtitleFormat + { + + private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d\:\d\d\:\d\d\s+\d+ ", RegexOptions.Compiled); + private static readonly Regex RegexNumberAndText = new Regex(@"^\d+ [^ ]+", RegexOptions.Compiled); + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 33"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + //08:59:51 3 ON THE PANEL THIS WEEK WE HAVE EMILY LAWLER AND ZACH + //08:59:54 4 GORCHOW, ALONG WITH RON DZWONKOWSKI. + // 5 HERE IS THE RUNDOWN. + // 6 A POSSIBLE REDO OF THE EM LAW IF VOTERS REJECT IT. + //09:00:03 7 AND MIKE DUGAN AND LATER GENE CLEM IS DISCUSSING THIS + + const string paragraphWriteFormat = "{0} {1} {2}"; + + var sb = new StringBuilder(); + int count = 1; + int count2 = 1; + foreach (Paragraph p in subtitle.Paragraphs) + { + var lines = HtmlUtil.RemoveHtmlTags(p.Text).SplitToLines(); + if (lines.Length > 0) + { + sb.AppendLine(string.Format(paragraphWriteFormat, EncodeTimeCode(p.StartTime), count.ToString(CultureInfo.InvariantCulture).PadLeft(2, ' '), lines[0])); + for (int i = 1; i < lines.Length; i++) + { + count++; + if (count > 26) + { + sb.Append(string.Empty.PadLeft(38, ' ') + count2); + sb.AppendLine(); + sb.AppendLine(); + count = 1; + count2++; + } + sb.AppendLine(string.Format(paragraphWriteFormat, string.Empty, count.ToString(CultureInfo.InvariantCulture).PadLeft(10, ' '), lines[i])); + } + + count++; + if (count > 26) + { + sb.Append(string.Empty.PadLeft(38, ' ') + count2); + sb.AppendLine(); + sb.AppendLine(); + count = 1; + count2++; + } + } + } + return sb.ToString().Trim(); + } + + private static string EncodeTimeCode(TimeCode timeCode) + { + int seconds = (int)(timeCode.Seconds + timeCode.Milliseconds / 1000 + 0.5); + return string.Format("{0:00}:{1:00}:{2:00}", timeCode.Hours, timeCode.Minutes, seconds); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + Paragraph p = null; + foreach (string line in lines) + { + string s = line.Trim(); + if (s.Length > 4 && s[2] == ':' && RegexTimeCodes.Match(s).Success) + { + if (p != null && !string.IsNullOrEmpty(p.Text)) + subtitle.Paragraphs.Add(p); + p = new Paragraph(); + + try + { + string[] arr = s.Substring(0, 8).Split(':'); + if (arr.Length == 3) + { + int hours = int.Parse(arr[0]); + int minutes = int.Parse(arr[1]); + int seconds = int.Parse(arr[2]); + p.StartTime = new TimeCode(hours, minutes, seconds, 0); + string text = s.Remove(0, 12).Trim(); + p.Text = text; + } + } + catch + { + _errorCount++; + } + } + else if (p != null && RegexNumberAndText.Match(s).Success) + { + if (p.Text.Length > 1000) + { + _errorCount += 100; + return; + } + string text = s.Remove(0, 2).Trim(); + p.Text = (p.Text + Environment.NewLine + text).Trim(); + } + else if (s.Length > 0 && !Utilities.IsInteger(s)) + { + _errorCount++; + } + } + if (p != null && !string.IsNullOrEmpty(p.Text)) + subtitle.Paragraphs.Add(p); + + int index = 1; + foreach (Paragraph paragraph in subtitle.Paragraphs) + { + Paragraph next = subtitle.GetParagraphOrDefault(index); + if (next != null) + { + paragraph.EndTime.TotalMilliseconds = next.StartTime.TotalMilliseconds - 1; + } + index++; + } + + subtitle.RemoveEmptyLines(); + subtitle.Renumber(); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/UnknownSubtitle34.cs b/libse/SubtitleFormats/UnknownSubtitle34.cs new file mode 100644 index 000000000..30ba5aa61 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle34.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle34 : SubtitleFormat + { + + private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d\:\d\d\:\d\d\t[^ ]+", RegexOptions.Compiled); + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 34"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + //08:55:05 >>> WELCOME BACK. + //08:59:49 """OFF THE RECORD"" STARTS RIGHT NOW." + //08:59:51 ON THE PANEL THIS WEEK WE HAVE EMILY LAWLER AND ZACH + //08:59:54 "GORCHOW, ALONG WITH RON DZWONKOWSKI." + // HERE IS THE RUNDOWN. + // A POSSIBLE REDO OF THE EM LAW IF VOTERS REJECT IT. + //09:00:03 AND MIKE DUGAN AND LATER GENE CLEM IS DISCUSSING THIS + + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + var lines = HtmlUtil.RemoveHtmlTags(p.Text).SplitToLines(); + if (lines.Length > 0) + { + sb.AppendLine(EncodeTimeCode(p.StartTime) + "\t" + lines[0]); + for (int i = 1; i < lines.Length; i++) + sb.AppendLine("\t" + lines[i]); + } + } + return sb.ToString().Trim(); + } + + private static string EncodeTimeCode(TimeCode timeCode) + { + int seconds = (int)(timeCode.Seconds + timeCode.Milliseconds / 1000 + 0.5); + return string.Format("{0:00}:{1:00}:{2:00}", timeCode.Hours, timeCode.Minutes, seconds); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + Paragraph p = null; + foreach (string line in lines) + { + string s = line.Trim(); + if (RegexTimeCodes.Match(s).Success && !UnknownSubtitle59.RegexTimeCodes.IsMatch(s)) + { + if (p != null && !string.IsNullOrEmpty(p.Text)) + subtitle.Paragraphs.Add(p); + p = new Paragraph(); + + try + { + string[] arr = s.Substring(0, 8).Split(':'); + if (arr.Length == 3) + { + int hours = int.Parse(arr[0]); + int minutes = int.Parse(arr[1]); + int seconds = int.Parse(arr[2]); + p.StartTime = new TimeCode(hours, minutes, seconds, 0); + string text = s.Remove(0, 8).Trim(); + p.Text = text; + if (text.Length > 1 && Utilities.IsInteger(text.Substring(0, 2))) + _errorCount++; + } + } + catch + { + _errorCount++; + } + } + else if (line.StartsWith("\t") && p != null) + { + if (p.Text.Length > 1000) + { + _errorCount += 100; + return; + } + p.Text = (p.Text + Environment.NewLine + s).Trim(); + } + else if (s.Length > 0 && !Utilities.IsInteger(s)) + { + _errorCount++; + } + } + if (p != null && !string.IsNullOrEmpty(p.Text)) + subtitle.Paragraphs.Add(p); + + int index = 1; + foreach (Paragraph paragraph in subtitle.Paragraphs) + { + Paragraph next = subtitle.GetParagraphOrDefault(index); + if (next != null) + { + paragraph.EndTime.TotalMilliseconds = next.StartTime.TotalMilliseconds - 1; + } + else + { + paragraph.EndTime.TotalMilliseconds = paragraph.StartTime.TotalMilliseconds + Utilities.GetOptimalDisplayMilliseconds(paragraph.Text); + } + index++; + } + + subtitle.RemoveEmptyLines(); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/UnknownSubtitle35.cs b/libse/SubtitleFormats/UnknownSubtitle35.cs new file mode 100644 index 000000000..7e42a1cfc --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle35.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle35 : SubtitleFormat + { + + //0072.08-0076.05 + //Pidin täna peaaegu surma saama, + //kuna röövisid vale koera. + + //0076.09-0078.14 + //Mõtled seda tõsiselt või? + + private static readonly Regex RegexTimeCode = new Regex(@"^\d\d\d\d\.\d\d-\d\d\d\d\.\d\d$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 35"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + private static string MakeTimeCode(TimeCode tc) + { + return string.Format("{0:0000}.{1:00}", (int)tc.TotalSeconds, MillisecondsToFramesMaxFrameRate(tc.Milliseconds)); + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + for (int i = 0; i < subtitle.Paragraphs.Count; i++) + { + Paragraph p = subtitle.Paragraphs[i]; + string text = HtmlUtil.RemoveHtmlTags(p.Text); + sb.AppendLine(string.Format("{0}-{1}\r\n{2}\r\n", MakeTimeCode(p.StartTime), MakeTimeCode(p.EndTime), text)); + } + return sb.ToString(); + } + + private static TimeCode DecodeTimeCode(string timeCode) + { + string[] arr = timeCode.Split(new[] { ':', ';', ',', '.' }, StringSplitOptions.RemoveEmptyEntries); + return new TimeCode(0, 0, int.Parse(arr[0]), FramesToMillisecondsMax999(int.Parse(arr[1]))); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + Paragraph p = null; + var sb = new StringBuilder(); + foreach (string line in lines) + { + string s = line.Trim(); + if (RegexTimeCode.IsMatch(s)) + { + try + { + if (p != null) + { + p.Text = sb.ToString().Trim(); + subtitle.Paragraphs.Add(p); + } + sb = new StringBuilder(); + string[] arr = s.Split('-'); + if (arr.Length == 2) + p = new Paragraph(DecodeTimeCode(arr[0]), DecodeTimeCode(arr[1]), string.Empty); + } + catch + { + _errorCount++; + p = null; + } + } + else if (!string.IsNullOrWhiteSpace(s)) + { + sb.AppendLine(s); + } + } + if (p != null) + { + p.Text = sb.ToString().Trim(); + subtitle.Paragraphs.Add(p); + } + subtitle.Renumber(); + } + + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle36.cs b/libse/SubtitleFormats/UnknownSubtitle36.cs new file mode 100644 index 000000000..d73d2c31d --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle36.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + + //00:00:31:17 , 00:00:35:12 , Signori e Signorine come state?|Va tutto bene? + + //00:00:35:24 , 00:00:40:17 ,Oggi riceveremo un |grande artista che viene dall’Africa. + + //00:00:40:20 , 00:00:44:12 ,Indovinate come si chiama?|Indovinate come si chiama? + + //00:00:44:15 , 00:00:48:24 ,Si chiama Bella Balde. + + //00:00:49:04 , 00:00:50:16 ,Grazie Signore e Signori. + + //00:00:50:18 , 00:00:52:24 ,per ricevere questo grande artista| che viene dall’Africa. + + //00:00:53:00 , 00:00:55:08 ,Grazie di nuovo,|grazie ancora. + + //--------------------------------------------------- + + //00:02:13:14 , 00:02:18:21 ,Stiamo preparando un festival|nel centro scolastico. + + //00:02:20:16 , 00:02:24:00 ,Gente di questo quartiere |Un gran festival! + + public class UnknownSubtitle36 : SubtitleFormat + { + private static readonly Regex RegexTimeCodes = new Regex(@"^\d+:\d+:\d+:\d+ , \d+:\d+:\d+:\d+ ,.*$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 36"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + if (lines.Count > 0 && lines[0] != null && lines[0].StartsWith("{\\rtf1")) + return false; + + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string paragraphWriteFormat = "{0} , {1} ,{2}\r\n"; + const string timeFormat = "{0:00}:{1:00}:{2:00}:{3:00}"; + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + string startTime = string.Format(timeFormat, p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, MillisecondsToFramesMaxFrameRate(p.StartTime.Milliseconds)); + string endTime = string.Format(timeFormat, p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, MillisecondsToFramesMaxFrameRate(p.EndTime.Milliseconds)); + sb.AppendFormat(paragraphWriteFormat, startTime, endTime, HtmlUtil.RemoveHtmlTags(p.Text.Replace(Environment.NewLine, " | "))); + } + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + int number = 0; + foreach (string line in lines) + { + if (string.IsNullOrWhiteSpace(line) || string.IsNullOrWhiteSpace(line.Trim('-'))) + { + continue; + } + + if (RegexTimeCodes.Match(line).Success) + { + string[] threePart = line.Split(new[] { ',' }, StringSplitOptions.None); + var p = new Paragraph(); + if (threePart.Length > 2 && + line.Length > 58 && + GetTimeCode(p.StartTime, threePart[0].Trim()) && + GetTimeCode(p.EndTime, threePart[1].Trim())) + { + number++; + p.Number = number; + p.Text = line.Remove(0, 57).Trim().Replace(" | ", Environment.NewLine).Replace("|", Environment.NewLine); + subtitle.Paragraphs.Add(p); + } + } + else + { + _errorCount++; + } + } + } + + private static bool GetTimeCode(TimeCode timeCode, string timeString) + { + try + { + string[] timeParts = timeString.Split(':'); + timeCode.Hours = int.Parse(timeParts[0]); + timeCode.Minutes = int.Parse(timeParts[1]); + timeCode.Seconds = int.Parse(timeParts[2]); + timeCode.Milliseconds = FramesToMillisecondsMax999(int.Parse(timeParts[3])); + return true; + } + catch + { + return false; + } + } + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle37.cs b/libse/SubtitleFormats/UnknownSubtitle37.cs new file mode 100644 index 000000000..8a0bde78b --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle37.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle37 : UnknownSubtitle36 + { + public override string Extension + { + get { return ".rtf"; } + } + + public override string Name + { + get { return "Unknown 37"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + if (fileName != null && !fileName.EndsWith(Extension, StringComparison.OrdinalIgnoreCase)) + return false; + + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + System.Windows.Forms.RichTextBox rtBox = new System.Windows.Forms.RichTextBox(); + rtBox.Text = base.ToText(subtitle, title); + string rtf = rtBox.Rtf; + rtBox.Dispose(); + return rtf; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + var sb = new StringBuilder(); + foreach (string line in lines) + sb.AppendLine(line); + + string rtf = sb.ToString().Trim(); + if (!rtf.StartsWith("{\\rtf")) + return; + + string[] arr = null; + var rtBox = new System.Windows.Forms.RichTextBox(); + try + { + rtBox.Rtf = rtf; + arr = rtBox.Text.Replace("\r\n", "\n").Split('\n'); + } + catch (Exception exception) + { + System.Diagnostics.Debug.WriteLine(exception.Message); + return; + } + finally + { + rtBox.Dispose(); + } + + var list = new List(); + foreach (string s in arr) + list.Add(s); + base.LoadSubtitle(subtitle, list, fileName); + } + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle38.cs b/libse/SubtitleFormats/UnknownSubtitle38.cs new file mode 100644 index 000000000..3fd28d353 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle38.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle38 : SubtitleFormat + { + private static readonly Regex RegexTimeCodes = new Regex(@"^\d+ \d\d:\d\d:\d\d:\d\d \d\d:\d\d:\d\d:\d\d$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 38"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + int index = 0; + foreach (Paragraph p in subtitle.Paragraphs) + { + //1 00:50:34:22 00:50:39:13 + //Ich muss dafür sorgen, + //dass die Epsteins weiterleben + sb.AppendLine(string.Format("{4} {0} {1}{2}{3}{2}", EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), Environment.NewLine, HtmlUtil.RemoveHtmlTags(p.Text), index)); + index++; + } + return sb.ToString(); + } + + private static string EncodeTimeCode(TimeCode time) + { + //1 00:50:39:13 (last is frame) + return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", time.Hours, time.Minutes, time.Seconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + //1 00:03:15:22 00:03:23:10 + //This is line one. + //This is line two. + Paragraph p = null; + subtitle.Paragraphs.Clear(); + _errorCount = 0; + foreach (string line in lines) + { + if (RegexTimeCodes.IsMatch(line)) + { + string[] temp = line.Split(' '); + if (temp.Length == 3) + { + string start = temp[1]; + string end = temp[2]; + + string[] startParts = start.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + string[] endParts = end.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + if (startParts.Length == 4 && endParts.Length == 4) + { + p = new Paragraph(DecodeTimeCode(startParts), DecodeTimeCode(endParts), string.Empty); + subtitle.Paragraphs.Add(p); + } + } + } + else if (string.IsNullOrWhiteSpace(line)) + { + // skip these lines + } + else if (p != null) + { + if (string.IsNullOrEmpty(p.Text)) + p.Text = line; + else + p.Text = p.Text + Environment.NewLine + line; + } + } + + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + //00:00:07:12 + string hour = parts[0]; + string minutes = parts[1]; + string seconds = parts[2]; + string frames = parts[3]; + TimeCode tc = new TimeCode(int.Parse(hour), int.Parse(minutes), int.Parse(seconds), FramesToMillisecondsMax999(int.Parse(frames))); + return tc; + } + + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle39.cs b/libse/SubtitleFormats/UnknownSubtitle39.cs new file mode 100644 index 000000000..9fe136179 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle39.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle39 : UnknownSubtitle38 + { + public override string Extension + { + get { return ".rtf"; } + } + + public override string Name + { + get { return "Unknown 39"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + if (fileName != null && !fileName.EndsWith(Extension, StringComparison.OrdinalIgnoreCase)) + return false; + + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + System.Windows.Forms.RichTextBox rtBox = new System.Windows.Forms.RichTextBox(); + rtBox.Text = base.ToText(subtitle, title); + string rtf = rtBox.Rtf; + rtBox.Dispose(); + return rtf; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + var sb = new StringBuilder(); + foreach (string line in lines) + sb.AppendLine(line); + + string rtf = sb.ToString().Trim(); + if (!rtf.StartsWith("{\\rtf")) + return; + + string[] arr = null; + var rtBox = new System.Windows.Forms.RichTextBox(); + try + { + rtBox.Rtf = rtf; + arr = rtBox.Text.Replace("\r\n", "\n").Split('\n'); + } + catch (Exception exception) + { + System.Diagnostics.Debug.WriteLine(exception.Message); + return; + } + finally + { + rtBox.Dispose(); + } + + var list = new List(); + foreach (string s in arr) + list.Add(s); + base.LoadSubtitle(subtitle, list, fileName); + } + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle4.cs b/libse/SubtitleFormats/UnknownSubtitle4.cs new file mode 100644 index 000000000..e47055273 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle4.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle4 : SubtitleFormat + { + private readonly static Regex RegexTimeCodes = new Regex(@"^\d\d:\d\d:\d\d.\d+, \d\d:\d\d:\d\d.\d+$", RegexOptions.Compiled); + + private enum ExpectingLine + { + TimeCodes, + Text + } + + public override string Extension + { + get { return ".sub"; } + } + + public override string Name + { + get { return "Unknown 4"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string paragraphWriteFormat = "{0:00}:{1:00}:{2:00}.{3:00}, {4:00}:{5:00}:{6:00}.{7:00}{8}{9}"; + + //00:00:07.00, 00:00:12.00 + //Welche Auswirkung Mikroversicherungen auf unsere Klienten hat? Lassen wir sie für sich selber sprechen! + // + //00:00:22.00, 00:00:27.00 + //Arme Menschen in Uganda leben oft in schlechten Unterkünften. + + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + string text = p.Text.Replace(Environment.NewLine, "|"); + + sb.AppendLine(string.Format(paragraphWriteFormat, + p.StartTime.Hours, + p.StartTime.Minutes, + p.StartTime.Seconds, + RoundTo2Cifres(p.StartTime.Milliseconds), + p.EndTime.Hours, + p.EndTime.Minutes, + p.EndTime.Seconds, + RoundTo2Cifres(p.EndTime.Milliseconds), + Environment.NewLine, + text)); + sb.AppendLine(); + } + return sb.ToString().Trim(); + } + + private static int RoundTo2Cifres(int milliseconds) + { + int rounded = (int)Math.Round((double)milliseconds / 10); + return rounded; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + var paragraph = new Paragraph(); + ExpectingLine expecting = ExpectingLine.TimeCodes; + _errorCount = 0; + + subtitle.Paragraphs.Clear(); + char[] splitChars = { ':', ',', '.', ' ' }; + foreach (string line in lines) + { + if (line.IndexOf(':') == 2 && RegexTimeCodes.IsMatch(line)) + { + string[] parts = line.Split(splitChars, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 8) + { + try + { + int startHours = int.Parse(parts[0]); + int startMinutes = int.Parse(parts[1]); + int startSeconds = int.Parse(parts[2]); + int startMilliseconds = int.Parse(parts[3]); + int endHours = int.Parse(parts[4]); + int endMinutes = int.Parse(parts[5]); + int endSeconds = int.Parse(parts[6]); + int endMilliseconds = int.Parse(parts[7]); + paragraph.StartTime = new TimeCode(startHours, startMinutes, startSeconds, startMilliseconds); + paragraph.EndTime = new TimeCode(endHours, endMinutes, endSeconds, endMilliseconds); + expecting = ExpectingLine.Text; + } + catch + { + expecting = ExpectingLine.TimeCodes; + } + } + } + else + { + if (expecting == ExpectingLine.Text) + { + if (line.Length > 0) + { + string text = line.Replace("|", Environment.NewLine); + text = line.Replace("[br]", Environment.NewLine); + text = line.Replace("
", Environment.NewLine); + text = line.Replace("
", Environment.NewLine); + text = text.Replace("{\\i1}", ""); + text = text.Replace("{\\i0}", ""); + text = text.Replace("{\\i}", "
"); + text = text.Replace("{\\b1}", "'"); + text = text.Replace("{\\b0}", ""); + text = text.Replace("{\\b}", ""); + text = text.Replace("{\\u1}", ""); + text = text.Replace("{\\u0}", ""); + text = text.Replace("{\\u}", ""); + + paragraph.Text = text; + subtitle.Paragraphs.Add(paragraph); + paragraph = new Paragraph(); + expecting = ExpectingLine.TimeCodes; + } + } + } + } + subtitle.Renumber(); + } + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle40.cs b/libse/SubtitleFormats/UnknownSubtitle40.cs new file mode 100644 index 000000000..d348f5ee1 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle40.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle40 : SubtitleFormat + { + // 0:01 – 0:03 + private static readonly Regex RegexTimeCodes = new Regex(@"^\d+:\d\d – \d+:\d\d$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 40"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + int index = 0; + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.AppendLine(string.Format("{0} – {1}{2}{3}", EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), Environment.NewLine, HtmlUtil.RemoveHtmlTags(p.Text))); + index++; + } + return sb.ToString(); + } + + private static string EncodeTimeCode(TimeCode time) + { + //00:50 + int seconds = time.Seconds; + if (time.Milliseconds >= 500) + seconds++; + return string.Format("{0}:{1:00}", time.Hours * 60 + time.Minutes, time.Seconds); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + Paragraph p = null; + subtitle.Paragraphs.Clear(); + _errorCount = 0; + char[] splitChar = { ':' }; + foreach (string line in lines) + { + if (RegexTimeCodes.IsMatch(line)) + { + string[] temp = line.Split('–'); + string start = temp[0].Trim(); + string end = temp[1].Trim(); + + string[] startParts = start.Split(splitChar, StringSplitOptions.RemoveEmptyEntries); + string[] endParts = end.Split(splitChar, StringSplitOptions.RemoveEmptyEntries); + if (startParts.Length == 2 && endParts.Length == 2) + { + p = new Paragraph(DecodeTimeCode(startParts), DecodeTimeCode(endParts), string.Empty); + subtitle.Paragraphs.Add(p); + } + } + else if (string.IsNullOrWhiteSpace(line)) + { + // skip these lines + } + else if (p != null) + { + if (string.IsNullOrEmpty(p.Text)) + p.Text = line; + else + p.Text = p.Text.TrimEnd() + Environment.NewLine + line; + if (p.Text.Length > 500) + return; + } + } + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + //00:00:07:12 + var minutes = int.Parse(parts[0]); + var seconds = int.Parse(parts[1]); + return new TimeCode(0, minutes, seconds, 0); + } + + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle41.cs b/libse/SubtitleFormats/UnknownSubtitle41.cs new file mode 100644 index 000000000..4fe2cb1b0 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle41.cs @@ -0,0 +1,128 @@ +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle41 : SubtitleFormat + { + private static readonly Regex RegexTimeCodes1 = new Regex(@"^\d+.\d$", RegexOptions.Compiled); + private static readonly Regex RegexTimeCodes2 = new Regex(@"^\d+.\d\d$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 41"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + Subtitle subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string paragraphWriteFormat = "{0}\r\n{1}\r\n{2}\r\n"; + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.AppendLine(string.Format(paragraphWriteFormat, EncodeTimeCode(p.StartTime), p.Text, EncodeTimeCode(p.EndTime))); + } + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + //911.2 + //C’est l’enfant qui l’a tuée ? + //915.8/ + + //921.8 + //Comment elle s’appelait ? + //924.6/ + + _errorCount = 0; + Paragraph p = null; + bool textOn = false; + var sb = new StringBuilder(); + foreach (string line in lines) + { + try + { + if (textOn) + { + if (RegexTimeCodes1.Match(line.TrimEnd('/')).Success || RegexTimeCodes2.Match(line).Success) + { + p.EndTime = DecodeTimeCode(line.TrimEnd('/').Split('.')); + if (sb.Length > 0) + { + p.Text = sb.ToString().TrimEnd(); + subtitle.Paragraphs.Add(p); + textOn = false; + } + } + else + { + sb.AppendLine(line); + if (sb.Length > 500) + { + _errorCount += 10; + return; + } + } + } + else + { + if (RegexTimeCodes1.Match(line).Success || RegexTimeCodes2.Match(line).Success) + { + p = new Paragraph(); + sb.Clear(); + p.StartTime = DecodeTimeCode(line.Split('.')); + textOn = true; + } + } + } + catch + { + textOn = false; + _errorCount++; + } + } + if (textOn && sb.Length > 0) + { + p.Text = sb.ToString().TrimEnd(); + subtitle.Paragraphs.Add(p); + } + subtitle.Renumber(); + } + + private static string EncodeTimeCode(TimeCode time) + { + Configuration.Settings.General.CurrentFrameRate = 24.0; + int frames = MillisecondsToFrames(time.TotalMilliseconds); + int footage = frames / 16; + int rest = (int)((frames % 16) / 16.0 * Configuration.Settings.General.CurrentFrameRate); + return string.Format("{0}.{1:0}", footage, rest); + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + Configuration.Settings.General.CurrentFrameRate = 24.0; + var frames16 = int.Parse(parts[0]); + var frames = int.Parse(parts[1]); + return new TimeCode(0, 0, 0, FramesToMilliseconds(16 * frames16 + frames)); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/UnknownSubtitle42.cs b/libse/SubtitleFormats/UnknownSubtitle42.cs new file mode 100644 index 000000000..b90626e6a --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle42.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle42 : SubtitleFormat + { + //SUB[0 I 01:00:09:10>01:00:12:10] + //SUB[0 N 01:00:09:10>01:00:12:10] + + // Time code line can optionally contain "speaker" + //SUB[0 N 01:02:02:03>01:02:03:06] VAL + //e eu tenho maiô pra nadar? + + // or multiple "speakers" seperated with a "+" + //SUB[0 N 01:02:12:26>01:02:14:19] FABINHO CRIANÇA + VAL + //-Olha. + //-Tô olhando! + private static readonly Regex RegexTimeCodesI = new Regex(@"^SUB\[\d I \d\d:\d\d:\d\d:\d\d\>\d\d:\d\d:\d\d:\d\d\]", RegexOptions.Compiled); + private static readonly Regex RegexTimeCodesN = new Regex(@"^SUB\[\d N \d\d:\d\d:\d\d:\d\d\>\d\d:\d\d:\d\d:\d\d\]", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 42"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + string style = "N"; + if (p.Text.StartsWith("", StringComparison.Ordinal) && p.Text.EndsWith("", StringComparison.Ordinal)) + style = "I"; + sb.AppendLine(string.Format("SUB[0 {0} {1}>{2}]{3}{4}", style, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), Environment.NewLine, HtmlUtil.RemoveHtmlTags(p.Text))); + sb.AppendLine(); + } + return sb.ToString().Trim(); + } + + private static string EncodeTimeCode(TimeCode time) + { + //00:50:39:13 (last is frame) + return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", time.Hours, time.Minutes, time.Seconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + //SUB[0 N 01:00:16:22>01:00:19:04] + //a cerca de 65 km a norte + //de Nova Iorque. + + _errorCount = 0; + Paragraph p = null; + subtitle.Paragraphs.Clear(); + bool italic = false; + foreach (string line in lines) + { + if (RegexTimeCodesI.IsMatch(line) || RegexTimeCodesN.IsMatch(line)) + { + if (p != null && italic) + p.Text = "" + p.Text.Trim() + ""; + + italic = line[6] == 'I'; + string start = line.Substring(8, 11); + string end = line.Substring(20, 11); + string[] startParts = start.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + string[] endParts = end.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + if (startParts.Length == 4 && endParts.Length == 4) + { + p = new Paragraph(DecodeTimeCode(startParts), DecodeTimeCode(endParts), string.Empty); + subtitle.Paragraphs.Add(p); + } + } + else if (string.IsNullOrWhiteSpace(line) || line.TrimStart().StartsWith('@')) + { + // skip these lines + } + else if (p != null) + { + if (string.IsNullOrEmpty(p.Text)) + p.Text = line; + else + p.Text = p.Text.TrimEnd() + Environment.NewLine + line; + } + } + if (p != null && italic) + p.Text = "" + p.Text.Trim() + ""; + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + //00:00:07:12 + var hour = int.Parse(parts[0]); + var minutes = int.Parse(parts[1]); + var seconds = int.Parse(parts[2]); + var frames = int.Parse(parts[3]); + return new TimeCode(hour, minutes, seconds, FramesToMillisecondsMax999(frames)); + } + + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle43.cs b/libse/SubtitleFormats/UnknownSubtitle43.cs new file mode 100644 index 000000000..53ff92cfc --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle43.cs @@ -0,0 +1,179 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle43 : SubtitleFormat + { + public override string Extension + { + get { return ".xml"; } + } + + public override string Name + { + get { return "Unknown 43"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + string xmlAsString = sb.ToString().Trim(); + if (xmlAsString.Contains(" 0; + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + } + } + return false; + } + + internal static string ConvertToTimeString(TimeCode time) + { + return string.Format("{0}:{1:00}", (int)(time.TotalSeconds / 60), (int)(time.TotalSeconds % 60)); + } + + public override string ToText(Subtitle subtitle, string title) + { + var xml = new XmlDocument(); + xml.LoadXml(""); + + int no = 0; + foreach (Paragraph p in subtitle.Paragraphs) + { + XmlNode paragraph = xml.CreateElement("subtitle"); + string text = p.Text; + + bool first = true; + foreach (string line in text.SplitToLines()) + { + if (!first) + { + XmlNode br = xml.CreateElement("br"); + paragraph.AppendChild(br); + } + var textNode = xml.CreateTextNode(string.Empty); + textNode.InnerText = line; + paragraph.AppendChild(textNode); + first = false; + } + + XmlAttribute end = xml.CreateAttribute("end"); + end.InnerText = ConvertToTimeString(p.EndTime); + paragraph.Attributes.Append(end); + + XmlAttribute start = xml.CreateAttribute("start"); + start.InnerText = ConvertToTimeString(p.StartTime); + paragraph.Attributes.Append(start); + + xml.DocumentElement.AppendChild(paragraph); + no++; + } + + return ToUtf8XmlString(xml).Replace(" xmlns=\"\"", string.Empty); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + var xml = new XmlDocument(); + xml.XmlResolver = null; + xml.LoadXml(sb.ToString().Trim()); + + foreach (XmlNode node in xml.DocumentElement.SelectNodes("subtitle")) + { + try + { + var pText = new StringBuilder(); + foreach (XmlNode innerNode in node.ChildNodes) + { + switch (innerNode.Name) + { + case "br": + pText.AppendLine(); + break; + case "span": + ReadSpan(pText, innerNode); + break; + + default: + pText.Append(innerNode.InnerText); + break; + } + } + + string start = node.Attributes["start"].InnerText; + string end = node.Attributes["end"].InnerText; + var p = new Paragraph(GetTimeCode(start), GetTimeCode(end), pText.ToString().Replace(" ", " ").Replace(" ", " ")); + subtitle.Paragraphs.Add(p); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + _errorCount++; + } + } + subtitle.Renumber(); + } + + private static void ReadSpan(StringBuilder pText, XmlNode innerNode) + { + if (innerNode.HasChildNodes) + { + foreach (XmlNode innerInnerNode in innerNode.ChildNodes) + { + if (innerInnerNode.Name == "br") + { + pText.AppendLine(); + } + else if (innerInnerNode.Name == "span") + { + ReadSpan(pText, innerInnerNode); + } + else + { + pText.Append(innerInnerNode.InnerText); + } + } + } + else + { + pText.Append(innerNode.InnerText); + } + } + + public static TimeCode GetTimeCode(string s) + { + string[] arr = s.Split(':'); + if (arr.Length == 2) + return new TimeCode(0, int.Parse(arr[0]), int.Parse(arr[1]), 0); + if (arr.Length == 3) + return new TimeCode(int.Parse(arr[0]), int.Parse(arr[1]), int.Parse(arr[2]), 0); + + return new TimeCode(0, 0, int.Parse(s), 0); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/UnknownSubtitle44.cs b/libse/SubtitleFormats/UnknownSubtitle44.cs new file mode 100644 index 000000000..a03d19d08 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle44.cs @@ -0,0 +1,152 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle44 : SubtitleFormat + { + + //>>> "COMMON GROUND" IS FUNDED BY 10:01:04:12 1 + //THE MINNESOTA ARTS AND CULTURAL 10:01:07:09 + private static Regex regexTimeCodes1 = new Regex(@" \d\d:\d\d:\d\d:\d\d$", RegexOptions.Compiled); + private static Regex regexTimeCodes2 = new Regex(@" \d\d:\d\d:\d\d:\d\d +\d+$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 44"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + + var sb = new StringBuilder(); + foreach (string line in lines) + sb.AppendLine(line); + + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + int index = 0; + int index2 = 0; + foreach (Paragraph p in subtitle.Paragraphs) + { + index++; + var text = new StringBuilder(); + text.Append(HtmlUtil.RemoveHtmlTags(p.Text.Replace(Environment.NewLine, " "))); + while (text.Length < 34) + text.Append(' '); + sb.AppendFormat("{0}{1}", text, EncodeTimeCode(p.StartTime)); + if (index % 50 == 1) + { + index2++; + sb.Append(new string(' ', 25) + index2); + } + sb.AppendLine(); + Paragraph next = subtitle.GetParagraphOrDefault(index); + if (next != null && next.StartTime.TotalMilliseconds - p.EndTime.TotalMilliseconds > 150) + { + text = new StringBuilder(); + while (text.Length < 34) + text.Append(' '); + sb.AppendLine(string.Format("{0}{1}", text, EncodeTimeCode(p.EndTime))); + } + } + return sb.ToString(); + } + + private static string EncodeTimeCode(TimeCode time) + { + return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", time.Hours, time.Minutes, time.Seconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + Paragraph p = null; + subtitle.Paragraphs.Clear(); + foreach (string line in lines) + { + string s = line.Trim(); + var match = regexTimeCodes2.Match(s); + if (match.Success) + { + s = s.Substring(0, match.Index + 13).Trim(); + } + match = regexTimeCodes1.Match(s); + if (match.Success && match.Index > 13) + { + string text = s.Substring(0, match.Index).Trim(); + string timeCode = s.Substring(match.Index).Trim(); + + string[] startParts = timeCode.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + if (startParts.Length == 4) + { + try + { + p = new Paragraph(DecodeTimeCode(startParts), new TimeCode(0, 0, 0, 0), text); + subtitle.Paragraphs.Add(p); + } + catch (Exception exception) + { + _errorCount++; + System.Diagnostics.Debug.WriteLine(exception.Message); + } + } + } + else if (string.IsNullOrWhiteSpace(line) || regexTimeCodes1.IsMatch(" " + s)) + { + // skip empty lines + } + else if (!string.IsNullOrWhiteSpace(line) && p != null) + { + _errorCount++; + } + } + + for (int i = 0; i < subtitle.Paragraphs.Count; i++) + { + Paragraph current = subtitle.Paragraphs[i]; + Paragraph next = subtitle.GetParagraphOrDefault(i + 1); + if (next != null) + current.EndTime.TotalMilliseconds = next.StartTime.TotalMilliseconds - Configuration.Settings.General.MinimumMillisecondsBetweenLines; + else + current.EndTime.TotalMilliseconds = current.StartTime.TotalMilliseconds + Utilities.GetOptimalDisplayMilliseconds(current.Text); + + if (current.Duration.TotalMilliseconds > Configuration.Settings.General.SubtitleMaximumDisplayMilliseconds) + current.EndTime.TotalMilliseconds = current.StartTime.TotalMilliseconds + Configuration.Settings.General.SubtitleMaximumDisplayMilliseconds; + } + subtitle.RemoveEmptyLines(); + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + //00:00:07:12 + string hour = parts[0]; + string minutes = parts[1]; + string seconds = parts[2]; + string frames = parts[3]; + + TimeCode tc = new TimeCode(int.Parse(hour), int.Parse(minutes), int.Parse(seconds), FramesToMillisecondsMax999(int.Parse(frames))); + return tc; + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/UnknownSubtitle45.cs b/libse/SubtitleFormats/UnknownSubtitle45.cs new file mode 100644 index 000000000..90afc4bd5 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle45.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle45 : SubtitleFormat + { + //* 00001.00-00003.00 02.01 00.0 1 0001 00 16-090-090 + //* 00138.10-00143.05 00.00 00.0 1 0003 00 16-090-090 + private static Regex regexTimeCodes = new Regex(@"^\*\s+\d\d\d\d\d\.\d\d-\d\d\d\d\d\.\d\d \d\d.\d\d \d\d.\d\ \d \d\d\d\d \d\d \d\d-\d\d\d-\d\d\d$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".rtf"; } + } + + public override string Name + { + get { return "Unknown 45"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + if (fileName != null && !fileName.EndsWith(Extension, StringComparison.OrdinalIgnoreCase)) + return false; + + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + sb.AppendLine(@"0 2 1.0 1.0 3.0 048 0400 0040 0500 100 100 0 100 0 6600 6600 01 +CRULIC R1 +ST 0 EB 3.10 +@"); + + int index = 0; + foreach (Paragraph p in subtitle.Paragraphs) + { + //1 00:50:34:22 00:50:39:13 + //Ich muss dafür sorgen, + //dass die Epsteins weiterleben + index++; + sb.AppendLine(string.Format("* {0}-{1} 00.00 00.0 1 {2} 00 16-090-090{3}{4}{3}@", EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), index.ToString().PadLeft(4, '0'), Environment.NewLine, HtmlUtil.RemoveHtmlTags(p.Text))); + } + System.Windows.Forms.RichTextBox rtBox = new System.Windows.Forms.RichTextBox(); + rtBox.Text = sb.ToString(); + string rtf = rtBox.Rtf; + rtBox.Dispose(); + return rtf; + } + + private static string EncodeTimeCode(TimeCode time) + { + return string.Format("{0:00000}.{1:00}", time.TotalSeconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + //* 00001.00-00003.00 02.01 00.0 1 0001 00 16-090-090 + //CRULIC R1 + //pour Bobi + //@ + _errorCount = 0; + var sb = new StringBuilder(); + foreach (string line in lines) + sb.AppendLine(line); + + string rtf = sb.ToString().Trim(); + if (!rtf.StartsWith("{\\rtf")) + return; + + string[] arr = null; + var rtBox = new System.Windows.Forms.RichTextBox(); + try + { + rtBox.Rtf = rtf; + arr = rtBox.Text.Replace("\r\n", "\n").Split('\n'); + } + catch (Exception exception) + { + System.Diagnostics.Debug.WriteLine(exception.Message); + return; + } + finally + { + rtBox.Dispose(); + } + + Paragraph p = null; + subtitle.Paragraphs.Clear(); + foreach (string line in arr) + { + if (regexTimeCodes.IsMatch(line.Trim())) + { + string[] temp = line.Substring(1).Trim().Substring(0, 17).Split('-'); + if (temp.Length == 2) + { + string start = temp[0]; + string end = temp[1]; + + string[] startParts = start.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries); + string[] endParts = end.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries); + if (startParts.Length == 2 && endParts.Length == 2) + { + p = new Paragraph(DecodeTimeCode(startParts), DecodeTimeCode(endParts), string.Empty); + subtitle.Paragraphs.Add(p); + } + } + } + else if (string.IsNullOrWhiteSpace(line) || line.Trim() == "@") + { + // skip these lines + } + else if (!string.IsNullOrWhiteSpace(line) && p != null) + { + if (p.Text.Length > 2000) + return; // wrong format + else if (string.IsNullOrEmpty(p.Text)) + p.Text = line; + else + p.Text = p.Text + Environment.NewLine + line; + } + } + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + //00119.12 + string seconds = parts[0]; + string frames = parts[1]; + TimeCode tc = new TimeCode(0, 0, int.Parse(seconds), FramesToMillisecondsMax999(int.Parse(frames))); + return tc; + } + + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle46.cs b/libse/SubtitleFormats/UnknownSubtitle46.cs new file mode 100644 index 000000000..bf31dd08a --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle46.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle46 : SubtitleFormat + { + //7:00:01:27AM + private static readonly Regex regexTimeCodesAM = new Regex(@"^\d\:\d\d\:\d\d\:\d\dAM", RegexOptions.Compiled); + private static readonly Regex regexTimeCodesPM = new Regex(@"^\d\:\d\d\:\d\d\:\d\dPM", RegexOptions.Compiled); + public override string Extension + { + get { return ".pst"; } + } + + public override string Name + { + get { return "Unknown 46"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + Subtitle subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + //OFF THE RECORD STARTS RIGHT NOW. 7:00:01:27AM + //HERE IS THE RUNDOWN. 7:00:05:03AM + var sb = new StringBuilder(); + const string format = "{0}{1}"; + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.AppendLine(string.Format(format, p.Text.Replace(Environment.NewLine, " ").PadRight(35), EncodeTimeCode(p.StartTime))); + } + return sb.ToString().Trim(); + } + + private static string EncodeTimeCode(TimeCode timeCode) + { + return string.Format("{0}:{1:00}:{2:00}:{3:00}AM", timeCode.Hours, timeCode.Minutes, timeCode.Seconds, MillisecondsToFramesMaxFrameRate(timeCode.Milliseconds)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + Paragraph p = null; + foreach (string line in lines) + { + string s = line.Trim(); + string[] arr = line.Split(); + var timeCode = arr[arr.Length - 1]; + if (regexTimeCodesAM.Match(timeCode).Success || regexTimeCodesPM.Match(timeCode).Success) + { + try + { + arr = timeCode.Substring(0, 10).Split(':'); + if (arr.Length == 4) + { + int hours = int.Parse(arr[0]); + int minutes = int.Parse(arr[1]); + int seconds = int.Parse(arr[2]); + int frames = int.Parse(arr[3]); + p = new Paragraph(); + p.StartTime = new TimeCode(hours, minutes, seconds, FramesToMillisecondsMax999(frames)); + p.Text = s.Substring(0, s.IndexOf(timeCode, StringComparison.Ordinal)).Trim(); + subtitle.Paragraphs.Add(p); + } + } + catch + { + _errorCount++; + } + } + else if (s.Length > 0) + { + _errorCount++; + } + } + + int index = 1; + foreach (Paragraph paragraph in subtitle.Paragraphs) + { + Paragraph next = subtitle.GetParagraphOrDefault(index); + if (next != null) + { + paragraph.EndTime.TotalMilliseconds = next.StartTime.TotalMilliseconds - 1; + } + if (paragraph.Duration.TotalMilliseconds > Configuration.Settings.General.SubtitleMaximumDisplayMilliseconds) + { + paragraph.EndTime.TotalMilliseconds = paragraph.StartTime.TotalMilliseconds + Utilities.GetOptimalDisplayMilliseconds(p.Text); + } + index++; + } + subtitle.RemoveEmptyLines(); + subtitle.Renumber(); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/UnknownSubtitle47.cs b/libse/SubtitleFormats/UnknownSubtitle47.cs new file mode 100644 index 000000000..5b1ab4dba --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle47.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle47 : SubtitleFormat + { + //7:00:01:27AM + private static Regex regexTimeCodes = new Regex(@"^\d\:\d\d\:\d\d\:\d\d\t", RegexOptions.Compiled); + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 47"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + Subtitle subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.AppendLine(string.Format("{0}\t{1}", EncodeTimeCode(p.StartTime), p.Text.Replace(Environment.NewLine, " "))); + } + return sb.ToString().Trim(); + } + + private static string EncodeTimeCode(TimeCode timeCode) + { + return string.Format("{0}:{1:00}:{2:00}:{3:00}", timeCode.Hours, timeCode.Minutes, timeCode.Seconds, MillisecondsToFramesMaxFrameRate(timeCode.Milliseconds)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + foreach (string line in lines) + { + string s = line.Trim(); + if (regexTimeCodes.Match(s).Success) + { + try + { + var arr = s.Substring(0, 10).Split(':'); + if (arr.Length == 4) + { + int hours = int.Parse(arr[0]); + int minutes = int.Parse(arr[1]); + int seconds = int.Parse(arr[2]); + int frames = int.Parse(arr[3]); + var p = new Paragraph(); + p.StartTime = new TimeCode(hours, minutes, seconds, FramesToMillisecondsMax999(frames)); + p.Text = s.Remove(0, 10).Trim(); + subtitle.Paragraphs.Add(p); + } + } + catch + { + _errorCount++; + } + } + else if (s.Length > 0) + { + _errorCount++; + } + } + + int index = 1; + foreach (Paragraph paragraph in subtitle.Paragraphs) + { + Paragraph next = subtitle.GetParagraphOrDefault(index); + if (next != null) + { + paragraph.EndTime.TotalMilliseconds = next.StartTime.TotalMilliseconds - 1; + } + if (paragraph.Duration.TotalMilliseconds > Configuration.Settings.General.SubtitleMaximumDisplayMilliseconds) + { + paragraph.EndTime.TotalMilliseconds = paragraph.StartTime.TotalMilliseconds + Utilities.GetOptimalDisplayMilliseconds(paragraph.Text); + } + index++; + } + + subtitle.RemoveEmptyLines(); + subtitle.Renumber(); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/UnknownSubtitle48.cs b/libse/SubtitleFormats/UnknownSubtitle48.cs new file mode 100644 index 000000000..3a95e23cb --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle48.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + + //00:01:27.703 00:01:29.514 Okay. + //00:01:29.259 00:01:31.514 Okaaayyyy. + //00:01:32.534 00:01:34.888 Let's go over this once again. + //00:01:35.446 00:01:38.346 Pick up the bread, walk the dog, go to the dry cleaners, + //00:01:38.609 00:01:41.471 pick up the bread, walk the dog, go thoughtless, + //00:01:42.247 00:01:43.915 pick up the cake + + public class UnknownSubtitle48 : SubtitleFormat + { + private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d:\d\d:\d\d.\d\d\d \d\d:\d\d:\d\d.\d\d\d .*$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 48"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + if (lines.Count > 0 && lines[0] != null && lines[0].StartsWith("{\\rtf1")) + return false; + + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string paragraphWriteFormat = "{0} {1} {2}"; + const string timeFormat = "{0:00}:{1:00}:{2:00}.{3:000}"; + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + string startTime = string.Format(timeFormat, p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, p.StartTime.Milliseconds); + string endTime = string.Format(timeFormat, p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, p.EndTime.Milliseconds); + sb.AppendLine(string.Format(paragraphWriteFormat, startTime, endTime, HtmlUtil.RemoveHtmlTags(p.Text.Replace(Environment.NewLine, " ")))); + } + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + foreach (string line in lines) + { + if (RegexTimeCodes.Match(line).Success) + { + string[] parts = line.Split(new[] { ' ' }, StringSplitOptions.None); + var p = new Paragraph(); + if (parts.Length > 2 && + GetTimeCode(p.StartTime, parts[0].Trim()) && + GetTimeCode(p.EndTime, parts[1].Trim())) + { + p.Text = line.Remove(0, 25).Trim(); + subtitle.Paragraphs.Add(p); + } + } + else + { + _errorCount += 10; + } + } + subtitle.Renumber(); + } + + private static bool GetTimeCode(TimeCode timeCode, string timeString) + { + try + { + string[] timeParts = timeString.Split(new[] { ':', '.' }); + timeCode.Hours = int.Parse(timeParts[0]); + timeCode.Minutes = int.Parse(timeParts[1]); + timeCode.Seconds = int.Parse(timeParts[2]); + timeCode.Milliseconds = int.Parse(timeParts[3]); + return true; + } + catch + { + return false; + } + } + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle49.cs b/libse/SubtitleFormats/UnknownSubtitle49.cs new file mode 100644 index 000000000..d54bd93f3 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle49.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle49 : SubtitleFormat + { + + private static readonly Regex RegexTimeCode = new Regex(@"^\d\d \d\d \d\d \d\d $", RegexOptions.Compiled); + private static readonly Regex RegexTimeCode2 = new Regex(@"^\d\d \d\d \d\d \d\d$", RegexOptions.Compiled); + + private enum ExpectingLine + { + TimeStart, + TimeEnd, + Text + } + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 49"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + //10 04 36 02 + //10 04 37 04 + // + //Greetings. + //10 04 37 06 + //10 04 40 08 + //It's confirmed, after reading + //Not Out on the poster.. + //10 04 40 15 + //10 04 44 06 + //..you have not come to pass you + //time, in this unique story. + + const string paragraphWriteFormat = "{0}{3}{1}{3}{2}"; + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + var text = HtmlUtil.RemoveOpenCloseTags(p.Text, HtmlUtil.TagFont); + if (!text.Contains(Environment.NewLine)) + text = Environment.NewLine + text; + sb.AppendLine(string.Format(paragraphWriteFormat, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), text, Environment.NewLine)); + } + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + Paragraph paragraph = null; + ExpectingLine expecting = ExpectingLine.TimeStart; + _errorCount = 0; + + subtitle.Paragraphs.Clear(); + foreach (string line in lines) + { + if (paragraph == null && expecting == ExpectingLine.TimeStart && (RegexTimeCode.IsMatch(line) || RegexTimeCode2.IsMatch(line))) + { + paragraph = new Paragraph(); + } + else if (paragraph != null && expecting == ExpectingLine.Text && (RegexTimeCode.IsMatch(line) || RegexTimeCode2.IsMatch(line))) + { + if (string.IsNullOrEmpty(paragraph.Text)) + _errorCount++; + if (paragraph.StartTime.TotalMilliseconds < 0.1) + _errorCount++; + subtitle.Paragraphs.Add(paragraph); + paragraph = new Paragraph(); + expecting = ExpectingLine.TimeStart; + } + + if (paragraph != null && expecting == ExpectingLine.TimeStart && (RegexTimeCode.IsMatch(line) || RegexTimeCode2.IsMatch(line))) + { + string[] parts = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 4) + { + try + { + var tc = DecodeTimeCode(parts); + paragraph.StartTime = tc; + expecting = ExpectingLine.TimeEnd; + } + catch + { + _errorCount++; + expecting = ExpectingLine.TimeStart; + } + } + } + else if (paragraph != null && expecting == ExpectingLine.TimeEnd && (RegexTimeCode.IsMatch(line) || RegexTimeCode2.IsMatch(line))) + { + string[] parts = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 4) + { + try + { + var tc = DecodeTimeCode(parts); + paragraph.EndTime = tc; + expecting = ExpectingLine.Text; + } + catch + { + _errorCount++; + expecting = ExpectingLine.TimeStart; + } + } + } + else + { + if (paragraph != null && expecting == ExpectingLine.Text) + { + if (line.Length > 0) + { + string s = line; + paragraph.Text = (paragraph.Text + Environment.NewLine + s).Trim(); + if (paragraph.Text.Length > 2000) + { + _errorCount += 100; + return; + } + } + } + } + } + if (paragraph != null && !string.IsNullOrEmpty(paragraph.Text)) + subtitle.Paragraphs.Add(paragraph); + subtitle.Renumber(); + } + + private static string EncodeTimeCode(TimeCode time) + { + return string.Format("{0:00} {1:00} {2:00} {3:00} ", time.Hours, time.Minutes, time.Seconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + string hour = parts[0]; + string minutes = parts[1]; + string seconds = parts[2]; + string frames = parts[3]; + return new TimeCode(int.Parse(hour), int.Parse(minutes), int.Parse(seconds), FramesToMillisecondsMax999(int.Parse(frames))); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/UnknownSubtitle5.cs b/libse/SubtitleFormats/UnknownSubtitle5.cs new file mode 100644 index 000000000..0ec33d4d5 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle5.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle5 : SubtitleFormat + { + public override string Extension + { + get { return ".xml"; } + } + + public override string Name + { + get { return "Unknown 5"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > 0; + } + + public override string ToText(Subtitle subtitle, string title) + { + string xmlStructure = + "" + Environment.NewLine + + ""; + + var xml = new XmlDocument(); + xml.LoadXml(xmlStructure); + + foreach (Paragraph p in subtitle.Paragraphs) + { + XmlNode paragraph = xml.CreateElement("text"); + + XmlAttribute start = xml.CreateAttribute("start"); + start.InnerText = string.Format("{0}", p.StartTime.TotalMilliseconds / 1000).Replace(",", "."); + paragraph.Attributes.Append(start); + + XmlAttribute duration = xml.CreateAttribute("dur"); + duration.InnerText = string.Format("{0}", p.Duration.TotalMilliseconds / 1000).Replace(",", "."); + paragraph.Attributes.Append(duration); + + paragraph.InnerText = p.Text; + + xml.DocumentElement.AppendChild(paragraph); + } + + return ToUtf8XmlString(xml); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + + string allText = sb.ToString(); + if (!allText.Contains(" lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string paragraphWriteFormat = "{0}-{1}\r\n{2}"; + var sb = new StringBuilder(); + sb.AppendLine(); + sb.AppendLine(); + sb.AppendLine(); + foreach (Paragraph p in subtitle.Paragraphs) + { + string text = p.Text; + if (Utilities.GetNumberOfLines(text) > 2) + text = Utilities.AutoBreakLine(text); + text = HtmlUtil.RemoveHtmlTags(text, true); + if (p.Text.Contains("")) + { + if (Utilities.CountTagInText(p.Text, "") == 1 && Utilities.CountTagInText(p.Text, "") == 1 && + p.Text.StartsWith("") && p.Text.StartsWith("")) + { + text = "||" + text.Replace(Environment.NewLine, "||" + Environment.NewLine + "||") + "||"; + } + else if (Utilities.CountTagInText(p.Text, "") == 2 && Utilities.CountTagInText(p.Text, "") == 2 && + p.Text.StartsWith("") && p.Text.StartsWith("") && p.Text.Contains("" + Environment.NewLine + "")) + { + text = "||" + text.Replace(Environment.NewLine, "||" + Environment.NewLine + "||") + "||"; + } + } + + if (!text.Contains(Environment.NewLine)) + text = Environment.NewLine + text; + sb.AppendLine(string.Format(paragraphWriteFormat, FormatTime(p.StartTime), FormatTime(p.EndTime), text)); + } + sb.AppendLine(); + return sb.ToString(); + } + + private static string FormatTime(TimeCode timeCode) + { + return string.Format("{0:00}.{1:00}.{2:00}.{3:00}", timeCode.Hours, timeCode.Minutes, timeCode.Seconds, MillisecondsToFramesMaxFrameRate(timeCode.Milliseconds)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + ExpectingLine expecting = ExpectingLine.TimeCodes; + Paragraph p = new Paragraph(); + expecting = ExpectingLine.TimeCodes; + _errorCount = 0; + + subtitle.Paragraphs.Clear(); + for (int i = 0; i < lines.Count; i++) + { + string line = lines[i]; + if (expecting == ExpectingLine.TimeCodes && RegexTimeCodes.IsMatch(line)) + { + if (p.Text.Length > 0 || p.EndTime.TotalMilliseconds > 0.1) + { + subtitle.Paragraphs.Add(p); + p = new Paragraph(); + } + if (TryReadTimeCodesLine(line, p)) + { + expecting = ExpectingLine.Text1; + } + else + { + _errorCount++; + } + } + else if (expecting == ExpectingLine.Text1) + { + if (p.Text.Length > 500) + { + _errorCount += 100; + return; + } + else + { + if (line.StartsWith("||")) + line = "" + line.Replace("||", string.Empty) + ""; + p.Text = line.Trim(); + expecting = ExpectingLine.Text2; + } + } + else if (expecting == ExpectingLine.Text2) + { + if (p.Text.Length > 500) + { + _errorCount += 100; + return; + } + else + { + if (line.StartsWith("||")) + line = "" + line.Replace("||", string.Empty) + ""; + p.Text = (p.Text + Environment.NewLine + line).Trim(); + expecting = ExpectingLine.TimeCodes; + } + } + } + if (!string.IsNullOrWhiteSpace(p.Text)) + subtitle.Paragraphs.Add(p); + + subtitle.Renumber(); + } + + private static bool TryReadTimeCodesLine(string line, Paragraph paragraph) + { + string[] parts = line.Replace("-", ".").Split('.'); + try + { + int startHours = int.Parse(parts[0]); + int startMinutes = int.Parse(parts[1]); + int startSeconds = int.Parse(parts[2]); + int startMilliseconds = int.Parse(parts[3]); + int endHours = int.Parse(parts[4]); + int endMinutes = int.Parse(parts[5]); + int endSeconds = int.Parse(parts[6]); + int endMilliseconds = int.Parse(parts[7]); + paragraph.StartTime = new TimeCode(startHours, startMinutes, startSeconds, startMilliseconds); + paragraph.EndTime = new TimeCode(endHours, endMinutes, endSeconds, endMilliseconds); + return true; + } + catch + { + return false; + } + } + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/UnknownSubtitle51.cs b/libse/SubtitleFormats/UnknownSubtitle51.cs new file mode 100644 index 000000000..9d47a2f59 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle51.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + + public class UnknownSubtitle51 : SubtitleFormat + { + private static readonly Regex RegexTimeCodes = new Regex(@"^\d+:\d+:\d+:\d+ , \d+:\d+:\d+:\d+ , .*$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 51"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + if (lines.Count > 0 && lines[0] != null && lines[0].StartsWith("{\\rtf1")) + return false; + + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string paragraphWriteFormat = "{0} , {1} , {2}\r\n"; + const string timeFormat = "{0:00}:{1:00}:{2:00}:{3:00}"; + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + string startTime = string.Format(timeFormat, p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, MillisecondsToFramesMaxFrameRate(p.StartTime.Milliseconds)); + string endTime = string.Format(timeFormat, p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, MillisecondsToFramesMaxFrameRate(p.EndTime.Milliseconds)); + sb.AppendFormat(paragraphWriteFormat, startTime, endTime, HtmlUtil.RemoveHtmlTags(p.Text.Replace(Environment.NewLine, " | "))); + } + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + int number = 0; + Paragraph p = null; + foreach (string line in lines) + { + if (string.IsNullOrWhiteSpace(line) || string.IsNullOrWhiteSpace(line.Trim('-'))) + { + continue; + } + + if (RegexTimeCodes.Match(line).Success) + { + string[] threePart = line.Split(new[] { ',' }, StringSplitOptions.None); + p = new Paragraph(); + if (threePart.Length > 2 && + line.Length > 32 && + GetTimeCode(p.StartTime, threePart[0].Trim()) && + GetTimeCode(p.EndTime, threePart[1].Trim())) + { + number++; + p.Number = number; + p.Text = line.Remove(0, 31).Trim().Replace(" | ", Environment.NewLine).Replace("|", Environment.NewLine); + subtitle.Paragraphs.Add(p); + } + } + else if (line.StartsWith("//")) + { + // comment + } + else if (p != null && p.Text.Length < 200) + { + p.Text = (p.Text + Environment.NewLine + line.Trim()).Trim(); + } + else + { + _errorCount++; + } + } + } + + private static bool GetTimeCode(TimeCode timeCode, string timeString) + { + try + { + string[] timeParts = timeString.Split(':'); + timeCode.Hours = int.Parse(timeParts[0]); + timeCode.Minutes = int.Parse(timeParts[1]); + timeCode.Seconds = int.Parse(timeParts[2]); + timeCode.Milliseconds = FramesToMillisecondsMax999(int.Parse(timeParts[3])); + return true; + } + catch + { + return false; + } + } + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle52.cs b/libse/SubtitleFormats/UnknownSubtitle52.cs new file mode 100644 index 000000000..48879cb66 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle52.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + + public class UnknownSubtitle52 : SubtitleFormat + { + //#00001 10:00:02.00 10:00:04.13 00:00:02.13 #F CC00000D0 #C + private static readonly Regex RegexTimeCodes = new Regex(@"^\#\d\d\d\d\d\t\d\d:\d\d:\d\d\.\d\d\t\d\d:\d\d:\d\d\.\d\d\t\d\d:\d\d:\d\d\.\d\d\t.*$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 52"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + if (lines.Count > 0 && lines[0] != null && lines[0].StartsWith("{\\rtf1")) + return false; + + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + string paragraphWriteFormat = "#{0:00000}\t{1}\t{2}\t{3}\t#F\tCC00000D0\t#C " + Environment.NewLine + "{4}"; + const string timeFormat = "{0:00}:{1:00}:{2:00}.{3:00}"; + var sb = new StringBuilder(); + string header = @"FILE_INFO_BEGIN +VIDEOFILE: +ORIG_TITLE: [TITLE] +PGM_TITLE: +EP_TITLE: 03 +PROD: +TRANSL: SDI Media +CLIENT: FIC-HD +COMMENT: +TAPE#: TN10179565 +CRE_DATE: +REP_DATE: +TR_DATE: +PROG_LEN: +SOM: 09:59:35:00 +TRA_FONT: +LANG_CO: English +LIST_FONT: Arial Unicode MS 450 +TV_SYS: 625/50 +TV_FPS: EBU 625/50 +LINE_LEN: 43.2 +SW_VER: 2.25 +FILE_INFO_END"; + if (subtitle.Header != null && subtitle.Header.Contains("FILE_INFO_BEGIN")) + header = subtitle.Header; + sb.AppendLine(header); + int number = 1; + foreach (Paragraph p in subtitle.Paragraphs) + { + var startFrame = MillisecondsToFramesMaxFrameRate(p.StartTime.Milliseconds); + string startTime = string.Format(timeFormat, p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, startFrame); + + var endFrame = MillisecondsToFramesMaxFrameRate(p.EndTime.Milliseconds); + string endTime = string.Format(timeFormat, p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, endFrame); + + // to avoid rounding errors in duration + var durationCalc = new Paragraph( + new TimeCode(p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, FramesToMillisecondsMax999(startFrame)), + new TimeCode(p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, FramesToMillisecondsMax999(endFrame)), + string.Empty); + string duration = string.Format(timeFormat, durationCalc.Duration.Hours, durationCalc.Duration.Minutes, durationCalc.Duration.Seconds, MillisecondsToFramesMaxFrameRate(durationCalc.Duration.Milliseconds)); + + sb.AppendLine(string.Format(paragraphWriteFormat, number, startTime, endTime, duration, HtmlUtil.RemoveHtmlTags(p.Text))); + number++; + } + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + Paragraph p = null; + bool started = false; + var header = new StringBuilder(); + var text = new StringBuilder(); + foreach (string line in lines) + { + try + { + if (RegexTimeCodes.Match(line).Success) + { + started = true; + if (p != null) + p.Text = text.ToString().Trim(); + text = new StringBuilder(); + string start = line.Substring(7, 11); + string end = line.Substring(19, 11); + p = new Paragraph(GetTimeCode(start), GetTimeCode(end), string.Empty); + subtitle.Paragraphs.Add(p); + } + else if (!started) + { + header.AppendLine(line); + } + else if (p != null && p.Text.Length < 200) + { + text.AppendLine(line); + } + else + { + _errorCount++; + } + } + catch + { + _errorCount++; + } + } + if (p != null) + p.Text = text.ToString().Trim(); + subtitle.Header = header.ToString(); + subtitle.RemoveEmptyLines(); + subtitle.Renumber(); + } + + private static TimeCode GetTimeCode(string timeString) + { + string[] timeParts = timeString.Split(new[] { ':', ',', '.' }); + int milliseconds = FramesToMillisecondsMax999(int.Parse(timeParts[3])); + var timeCode = new TimeCode(int.Parse(timeParts[0]), int.Parse(timeParts[1]), int.Parse(timeParts[2]), milliseconds); + return timeCode; + } + + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle53.cs b/libse/SubtitleFormats/UnknownSubtitle53.cs new file mode 100644 index 000000000..3e26be091 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle53.cs @@ -0,0 +1,117 @@ +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle53 : SubtitleFormat + { + + private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d\:\d\d\:\d\d\:\d\d [^ ]+", RegexOptions.Compiled); + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 53"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + Subtitle subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + //10:56:54:12 FEATURING BRIAN LORENTE AND THE + //10:56:59:18 USUAL SUSPECTS. + //10:57:15:18 \M + //10:57:20:07 >> HOW WE DOING TONIGHT, + //10:57:27:17 MICHIGAN? + + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + string text = HtmlUtil.RemoveHtmlTags(p.Text).Replace("♪", "\\M"); + sb.AppendLine(EncodeTimeCode(p.StartTime) + " " + text); + } + return sb.ToString().Trim(); + } + + private static string EncodeTimeCode(TimeCode timeCode) + { + return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", timeCode.Hours, timeCode.Minutes, timeCode.Seconds, MillisecondsToFramesMaxFrameRate(timeCode.Milliseconds)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + Paragraph p = null; + foreach (string line in lines) + { + string s = line.Trim(); + if (RegexTimeCodes.Match(s).Success) + { + if (p != null && !string.IsNullOrEmpty(p.Text)) + subtitle.Paragraphs.Add(p); + p = new Paragraph(); + + try + { + string[] arr = s.Substring(0, 11).Split(':'); + if (arr.Length == 4) + { + int hours = int.Parse(arr[0]); + int minutes = int.Parse(arr[1]); + int seconds = int.Parse(arr[2]); + int frames = int.Parse(arr[3]); + p.StartTime = new TimeCode(hours, minutes, seconds, FramesToMillisecondsMax999(frames)); + string text = s.Remove(0, 11).Trim(); + p.Text = text; + if (text.Length > 1 && Utilities.IsInteger(text.Substring(0, 2))) + _errorCount++; + } + } + catch + { + _errorCount++; + } + } + else if (s.Length > 0) + { + _errorCount++; + } + } + if (p != null && !string.IsNullOrEmpty(p.Text)) + subtitle.Paragraphs.Add(p); + + int index = 1; + foreach (Paragraph paragraph in subtitle.Paragraphs) + { + paragraph.Text = paragraph.Text.Replace("\\M", "♪"); + + Paragraph next = subtitle.GetParagraphOrDefault(index); + if (next != null) + { + paragraph.EndTime.TotalMilliseconds = next.StartTime.TotalMilliseconds - 1; + } + else + { + paragraph.EndTime.TotalMilliseconds = paragraph.StartTime.TotalMilliseconds + Utilities.GetOptimalDisplayMilliseconds(paragraph.Text); + } + index++; + } + subtitle.RemoveEmptyLines(); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/UnknownSubtitle54.cs b/libse/SubtitleFormats/UnknownSubtitle54.cs new file mode 100644 index 000000000..58151cd62 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle54.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle54 : SubtitleFormat + { + //10:00:31:01 + //10:00:33:02 + //This is the king's royal court. + + //10:00:33:19 + //10:00:35:00 + //This is the place, + private static readonly Regex RegexTimeCodes1 = new Regex(@"^\d\d:\d\d:\d\d:\d\d$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 54"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + + var sb = new StringBuilder(); + foreach (string line in lines) + sb.AppendLine(line); + + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.AppendLine(EncodeTimeCode(p.StartTime)); + sb.AppendLine(EncodeTimeCode(p.EndTime)); + sb.AppendLine(HtmlUtil.RemoveHtmlTags(p.Text)); + sb.AppendLine(); + } + return sb.ToString(); + } + + private static string EncodeTimeCode(TimeCode time) + { + return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", time.Hours, time.Minutes, time.Seconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + bool expectStartTime = true; + var p = new Paragraph(); + subtitle.Paragraphs.Clear(); + foreach (string line in lines) + { + string s = line.Trim(); + var match = RegexTimeCodes1.Match(s); + if (match.Success && s.Length == 11) + { + string[] parts = s.Split(':'); + if (parts.Length == 4) + { + try + { + if (expectStartTime) + { + p.StartTime = DecodeTimeCode(parts); + expectStartTime = false; + } + else + { + if (p.StartTime.TotalMilliseconds < 0.01) + _errorCount++; + if (!string.IsNullOrEmpty(p.Text)) + _errorCount++; + + p.EndTime = DecodeTimeCode(parts); + } + } + catch (Exception exception) + { + _errorCount++; + System.Diagnostics.Debug.WriteLine(exception.Message); + } + } + } + else if (string.IsNullOrWhiteSpace(line)) + { + if (p.StartTime.TotalMilliseconds == 0 && p.EndTime.TotalMilliseconds == 0) + _errorCount++; + else + subtitle.Paragraphs.Add(p); + p = new Paragraph(); + } + else if (!string.IsNullOrWhiteSpace(line)) + { + expectStartTime = true; + p.Text = (p.Text + Environment.NewLine + line).Trim(); + if (p.Text.Length > 500) + { + _errorCount += 10; + return; + } + } + } + if (p.EndTime.TotalMilliseconds > 0) + subtitle.Paragraphs.Add(p); + + bool allNullEndTime = true; + for (int i = 0; i < subtitle.Paragraphs.Count; i++) + { + if (subtitle.Paragraphs[i].EndTime.TotalMilliseconds != 0) + allNullEndTime = false; + } + if (allNullEndTime) + subtitle.Paragraphs.Clear(); + + subtitle.RemoveEmptyLines(); + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + //00:00:07:12 + string hour = parts[0]; + string minutes = parts[1]; + string seconds = parts[2]; + string frames = parts[3]; + + return new TimeCode(int.Parse(hour), int.Parse(minutes), int.Parse(seconds), FramesToMillisecondsMax999(int.Parse(frames))); + } + + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle55.cs b/libse/SubtitleFormats/UnknownSubtitle55.cs new file mode 100644 index 000000000..c6fc49b09 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle55.cs @@ -0,0 +1,165 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle55 : SubtitleFormat + { + + // 338: 00:24:34.00 00:24:37.10 [51] + private static readonly Regex RegexTimeCodes = new Regex(@"^\d+:\s+\d\d:\d\d:\d\d\.\d\d\s+\d\d:\d\d:\d\d\.\d\d\s+\[\d+\]$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".rtf"; } + } + + public override string Name + { + get { return "Unknown 55"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + + var sb = new StringBuilder(); + foreach (string line in lines) + sb.AppendLine(line); + + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string format = "{0}: {1} {2} [{3}]"; + var sb = new StringBuilder(); + int count = 1; + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.AppendLine(string.Format(format, count, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), p.Text.Length)); + sb.AppendLine(p.Text); + sb.AppendLine(); + count++; + } + + System.Windows.Forms.RichTextBox rtBox = new System.Windows.Forms.RichTextBox(); + rtBox.Text = sb.ToString(); + string rtf = rtBox.Rtf; + rtBox.Dispose(); + return rtf; + } + + private static string EncodeTimeCode(TimeCode time) + { + return string.Format("{0:00}:{1:00}:{2:00}.{3:00}", time.Hours, time.Minutes, time.Seconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + var sb = new StringBuilder(); + foreach (string line in lines) + sb.AppendLine(line); + + string rtf = sb.ToString().Trim(); + if (!rtf.StartsWith("{\\rtf")) + return; + + string[] arr = null; + var rtBox = new System.Windows.Forms.RichTextBox(); + try + { + rtBox.Rtf = rtf; + arr = rtBox.Text.Replace("\r\n", "\n").Replace("\r", "\n").Split('\n'); + } + catch (Exception exception) + { + System.Diagnostics.Debug.WriteLine(exception.Message); + return; + } + finally + { + rtBox.Dispose(); + } + + bool expectStartTime = true; + var p = new Paragraph(); + subtitle.Paragraphs.Clear(); + foreach (string line in arr) + { + string s = line.Trim().Replace("*", string.Empty); + var match = RegexTimeCodes.Match(s); + if (match.Success) + { + string[] parts = s.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 4) + { + try + { + if (!string.IsNullOrEmpty(p.Text)) + { + subtitle.Paragraphs.Add(p); + p = new Paragraph(); + } + p.StartTime = DecodeTimeCode(parts[1]); + p.EndTime = DecodeTimeCode(parts[2]); + expectStartTime = false; + } + catch (Exception exception) + { + _errorCount++; + System.Diagnostics.Debug.WriteLine(exception.Message); + } + } + } + else if (string.IsNullOrWhiteSpace(line)) + { + if (p.StartTime.TotalMilliseconds == 0 && p.EndTime.TotalMilliseconds == 0) + _errorCount++; + else + subtitle.Paragraphs.Add(p); + p = new Paragraph(); + } + else if (!expectStartTime) + { + p.Text = (p.Text + Environment.NewLine + line).Trim(); + if (p.Text.Length > 500) + { + _errorCount += 10; + return; + } + while (p.Text.Contains(Environment.NewLine + " ")) + p.Text = p.Text.Replace(Environment.NewLine + " ", Environment.NewLine); + } + } + if (!string.IsNullOrEmpty(p.Text)) + subtitle.Paragraphs.Add(p); + + subtitle.RemoveEmptyLines(); + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string part) + { + string[] parts = part.Split(new[] { '.', ':' }, StringSplitOptions.RemoveEmptyEntries); + + //00:00:07:12 + string hour = parts[0]; + string minutes = parts[1]; + string seconds = parts[2]; + string frames = parts[3]; + + return new TimeCode(int.Parse(hour), int.Parse(minutes), int.Parse(seconds), FramesToMillisecondsMax999(int.Parse(frames))); + } + + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle56.cs b/libse/SubtitleFormats/UnknownSubtitle56.cs new file mode 100644 index 000000000..e16337c3b --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle56.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle56 : SubtitleFormat + { + //0001 01:00:37:22 01:00:39:11 + private static readonly Regex RegexTimeCodes1 = new Regex(@"^\d\d\d\d\t\d\d:\d\d:\d\d:\d\d\t\d\d:\d\d:\d\d:\d\d$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 56"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + + var sb = new StringBuilder(); + foreach (string line in lines) + sb.AppendLine(line); + + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + const string format = "{0:0000}\t{1}\t{2}"; + int count = 1; + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.AppendLine(string.Format(format, 1, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime))); + sb.AppendLine(HtmlUtil.RemoveHtmlTags(p.Text)); + sb.AppendLine(); + count++; + } + return sb.ToString(); + } + + private static string EncodeTimeCode(TimeCode time) + { + return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", time.Hours, time.Minutes, time.Seconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + bool expectStartTime = true; + var p = new Paragraph(); + subtitle.Paragraphs.Clear(); + foreach (string line in lines) + { + string s = line.Trim(); + var match = RegexTimeCodes1.Match(s); + if (match.Success) + { + string[] parts = s.Split('\t'); + if (parts.Length == 3) + { + try + { + if (!string.IsNullOrEmpty(p.Text)) + { + subtitle.Paragraphs.Add(p); + p = new Paragraph(); + } + p.StartTime = DecodeTimeCode(parts[1]); + p.EndTime = DecodeTimeCode(parts[2]); + expectStartTime = false; + } + catch (Exception exception) + { + _errorCount++; + System.Diagnostics.Debug.WriteLine(exception.Message); + } + } + } + else if (string.IsNullOrWhiteSpace(line)) + { + if (p.StartTime.TotalMilliseconds == 0 && p.EndTime.TotalMilliseconds == 0) + _errorCount++; + else + subtitle.Paragraphs.Add(p); + p = new Paragraph(); + } + else if (!expectStartTime) + { + p.Text = (p.Text + Environment.NewLine + line).Trim(); + if (p.Text.Length > 500) + { + _errorCount += 10; + return; + } + while (p.Text.Contains(Environment.NewLine + " ")) + p.Text = p.Text.Replace(Environment.NewLine + " ", Environment.NewLine); + } + } + if (p.EndTime.TotalMilliseconds > 0) + subtitle.Paragraphs.Add(p); + + foreach (Paragraph temp in subtitle.Paragraphs) + temp.Text = temp.Text.Replace("<", "@ITALIC_START").Replace(">", "").Replace("@ITALIC_START", ""); + + subtitle.RemoveEmptyLines(); + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string part) + { + string[] parts = part.Split(new[] { '.', ':' }, StringSplitOptions.RemoveEmptyEntries); + + //00:00:07:12 + string hour = parts[0]; + string minutes = parts[1]; + string seconds = parts[2]; + string frames = parts[3]; + + return new TimeCode(int.Parse(hour), int.Parse(minutes), int.Parse(seconds), FramesToMillisecondsMax999(int.Parse(frames))); + } + + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle57.cs b/libse/SubtitleFormats/UnknownSubtitle57.cs new file mode 100644 index 000000000..6263dcb37 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle57.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle57 : SubtitleFormat + { + private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d:\d\d:\d\d\.\d\d \d\d:\d\d:\d\d\.\d\d .+", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 57"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + + var sb = new StringBuilder(); + foreach (string line in lines) + sb.AppendLine(line); + + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + int index = 0; + foreach (Paragraph p in subtitle.Paragraphs) + { + //00:00:54.08 00:00:58.06 - Saucers... - ... a dry lake bed. (newline is //) + sb.AppendLine(string.Format("{0} {1} {2}", EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), HtmlUtil.RemoveHtmlTags(p.Text).Replace(Environment.NewLine, "//"))); + index++; + } + return sb.ToString(); + } + + private static string EncodeTimeCode(TimeCode time) + { + //00:03:15.22 (last is frame) + return string.Format("{0:00}:{1:00}:{2:00}.{3:00}", time.Hours, time.Minutes, time.Seconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + //00:03:15.22 00:03:23.10 This is line one.//This is line two. + _errorCount = 0; + Paragraph p = null; + subtitle.Paragraphs.Clear(); + foreach (string line in lines) + { + if (RegexTimeCodes.IsMatch(line)) + { + string temp = line.Substring(0, RegexTimeCodes.Match(line).Length); + string start = temp.Substring(0, 11); + string end = temp.Substring(12, 11); + + string[] startParts = start.Split(new[] { ':', '.' }, StringSplitOptions.RemoveEmptyEntries); + string[] endParts = end.Split(new[] { ':', '.' }, StringSplitOptions.RemoveEmptyEntries); + if (startParts.Length == 4 && endParts.Length == 4 && line.Length >= 23) + { + string text = line.Remove(0, 23).Trim(); + if (!text.Contains(Environment.NewLine)) + text = text.Replace("//", Environment.NewLine); + p = new Paragraph(DecodeTimeCode(startParts), DecodeTimeCode(endParts), text); + subtitle.Paragraphs.Add(p); + } + } + else if (string.IsNullOrWhiteSpace(line)) + { + } + else if (p != null) + { + if (p.Text.Length < 200) + p.Text = (p.Text + Environment.NewLine + line).Trim(); + } + } + + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + string hour = parts[0]; + string minutes = parts[1]; + string seconds = parts[2]; + string frames = parts[3]; + + return new TimeCode(int.Parse(hour), int.Parse(minutes), int.Parse(seconds), FramesToMillisecondsMax999(int.Parse(frames))); + } + + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle58.cs b/libse/SubtitleFormats/UnknownSubtitle58.cs new file mode 100644 index 000000000..886986511 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle58.cs @@ -0,0 +1,160 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle58 : SubtitleFormat + { + + //[01:01:53:09] + private static readonly Regex RegexTimeCodes = new Regex(@"^\[\d\d:\d\d:\d\d:\d\d]$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".rtf"; } + } + + public override string Name + { + get { return "Unknown 58"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + + var sb = new StringBuilder(); + foreach (string line in lines) + sb.AppendLine(line); + + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string format = "[{0}]{3}{3}{2}{3}{3}[{1}]{3}"; + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.AppendLine(string.Format(format, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), p.Text, Environment.NewLine)); + } + + System.Windows.Forms.RichTextBox rtBox = new System.Windows.Forms.RichTextBox(); + rtBox.Text = sb.ToString(); + string rtf = rtBox.Rtf; + rtBox.Dispose(); + return rtf; + } + + private static string EncodeTimeCode(TimeCode time) + { + return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", time.Hours, time.Minutes, time.Seconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + var sb = new StringBuilder(); + foreach (string line in lines) + sb.AppendLine(line); + + string rtf = sb.ToString().Trim(); + if (!rtf.StartsWith("{\\rtf")) + return; + + string[] arr = null; + var rtBox = new System.Windows.Forms.RichTextBox(); + try + { + rtBox.Rtf = rtf; + arr = rtBox.Text.Replace("\r", "").Split('\n'); + } + catch (Exception exception) + { + System.Diagnostics.Debug.WriteLine(exception.Message); + return; + } + finally + { + rtBox.Dispose(); + } + + var p = new Paragraph(); + subtitle.Paragraphs.Clear(); + foreach (string line in arr) + { + string s = line.Trim(); + if (s.StartsWith('[') && s.EndsWith('>') && s.Length > 13 && s[12] == ']') + s = s.Substring(0, 13); + + var match = RegexTimeCodes.Match(s); + if (match.Success) + { + string[] parts = s.Replace("[", string.Empty).Replace("]", string.Empty).Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 1) + { + try + { + if (!string.IsNullOrEmpty(p.Text)) + { + p.EndTime = DecodeTimeCode(parts[0]); + subtitle.Paragraphs.Add(p); + p = new Paragraph(); + } + else + { + p.StartTime = DecodeTimeCode(parts[0]); + } + } + catch (Exception exception) + { + _errorCount++; + System.Diagnostics.Debug.WriteLine(exception.Message); + } + } + } + else if (string.IsNullOrWhiteSpace(line)) + { + } + else + { + p.Text = (p.Text + Environment.NewLine + line).Trim(); + if (p.Text.Length > 500) + { + _errorCount += 10; + return; + } + while (p.Text.Contains(Environment.NewLine + " ")) + p.Text = p.Text.Replace(Environment.NewLine + " ", Environment.NewLine); + } + } + if (!string.IsNullOrEmpty(p.Text)) + subtitle.Paragraphs.Add(p); + + subtitle.RemoveEmptyLines(); + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string part) + { + string[] parts = part.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + + //00:00:07:12 + string hour = parts[0]; + string minutes = parts[1]; + string seconds = parts[2]; + string frames = parts[3]; + + return new TimeCode(int.Parse(hour), int.Parse(minutes), int.Parse(seconds), FramesToMillisecondsMax999(int.Parse(frames))); + } + + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle59.cs b/libse/SubtitleFormats/UnknownSubtitle59.cs new file mode 100644 index 000000000..b9ebee179 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle59.cs @@ -0,0 +1,184 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle59 : SubtitleFormat + { + + public static readonly Regex RegexTimeCodes = new Regex(@"^\d\d\:\d\d\:\d\d\t.+\t\d\d\:\d\d\:\d\d$", RegexOptions.Compiled); + public static readonly Regex RegexTimeCodes2 = new Regex(@"^\d\d\:\d\d\:\d\d.+\d\d\:\d\d\:\d\d$", RegexOptions.Compiled); + private static readonly Regex RegexStartOnly = new Regex(@"^\d\d\:\d\d\:\d\d\t.+$", RegexOptions.Compiled); + private static readonly Regex RegexEndOnly = new Regex(@"\d\d\:\d\d\:\d\d$", RegexOptions.Compiled); + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 59"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + Subtitle subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + //00:06:12 Would you like to see any particular style? 00:06:13 + // + //00:35:46 I made coffee. Would you like some? 00:35:47 + //Yes. + // + + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + var lines = HtmlUtil.RemoveHtmlTags(p.Text).SplitToLines(); + if (lines.Length > 0) + { + sb.AppendLine(EncodeTimeCode(p.StartTime) + "\t" + lines[0]); + for (int i = 1; i < lines.Length; i++) + sb.AppendLine("\t" + lines[i]); + } + } + return sb.ToString().Trim(); + } + + private static string EncodeTimeCode(TimeCode timeCode) + { + int seconds = (int)(timeCode.Seconds + timeCode.Milliseconds / 1000 + 0.5); + return string.Format("{0:00}:{1:00}:{2:00}", timeCode.Hours, timeCode.Minutes, seconds); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + Paragraph p = null; + foreach (string line in lines) + { + string s = line.Trim(); + if (RegexTimeCodes.Match(s).Success || RegexTimeCodes2.IsMatch(s)) + { + if (RegexTimeCodes2.IsMatch(s)) + _errorCount++; + try + { + p = new Paragraph(); + string[] start = s.Substring(0, 8).Split(':'); + string[] end = s.Remove(0, s.Length - 8).Split(':'); + if (start.Length == 3) + { + int hours = int.Parse(start[0]); + int minutes = int.Parse(start[1]); + int seconds = int.Parse(start[2]); + p.StartTime = new TimeCode(hours, minutes, seconds, 0); + + hours = int.Parse(end[0]); + minutes = int.Parse(end[1]); + seconds = int.Parse(end[2]); + p.EndTime = new TimeCode(hours, minutes, seconds, 0); + + string text = s.Remove(0, 8).Trim(); + text = text.Substring(0, text.Length - 8).Trim(); + p.Text = text; + if (text.Length > 1 && Utilities.IsInteger(text.Substring(0, 2))) + _errorCount++; + subtitle.Paragraphs.Add(p); + } + } + catch + { + _errorCount++; + } + } + else if (RegexStartOnly.Match(s).Success) + { + try + { + p = new Paragraph(); + string[] start = s.Substring(0, 8).Split(':'); + if (start.Length == 3) + { + int hours = int.Parse(start[0]); + int minutes = int.Parse(start[1]); + int seconds = int.Parse(start[2]); + p.StartTime = new TimeCode(hours, minutes, seconds, 0); + + string text = s.Remove(0, 8).Trim(); + p.Text = text; + if (text.Length > 1 && Utilities.IsInteger(text.Substring(0, 2))) + _errorCount++; + subtitle.Paragraphs.Add(p); + } + } + catch + { + _errorCount++; + } + } + else if (RegexEndOnly.Match(s).Success) + { + try + { + string[] end = s.Remove(0, s.Length - 8).Split(':'); + if (end.Length == 3 && p != null) + { + int hours = int.Parse(end[0]); + int minutes = int.Parse(end[1]); + int seconds = int.Parse(end[2]); + p.EndTime = new TimeCode(hours, minutes, seconds, 0); + + string text = s.Substring(0, s.Length - 8).Trim(); + p.Text = p.Text + Environment.NewLine + text; + if (text.Length > 1 && Utilities.IsInteger(text.Substring(0, 2))) + _errorCount++; + p = null; + } + } + catch + { + _errorCount++; + } + } + else if (line.StartsWith("\t") && p != null) + { + if (p.Text.Length > 1000) + { + _errorCount += 100; + return; + } + p.Text = (p.Text + Environment.NewLine + s).Trim(); + } + else if (s.Length > 0 && !Utilities.IsInteger(s)) + { + if (p != null && !p.Text.Contains(Environment.NewLine)) + p.Text = p.Text + Environment.NewLine + s.Trim(); + else + _errorCount++; + } + } + + foreach (Paragraph p2 in subtitle.Paragraphs) + { + if (p2.Duration.TotalMilliseconds > Configuration.Settings.General.SubtitleMaximumDisplayMilliseconds) + p2.EndTime.TotalMilliseconds = p2.StartTime.TotalMilliseconds + Configuration.Settings.General.SubtitleMaximumDisplayMilliseconds; + p2.Text = Utilities.AutoBreakLine(p2.Text); + } + + subtitle.Renumber(); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/UnknownSubtitle6.cs b/libse/SubtitleFormats/UnknownSubtitle6.cs new file mode 100644 index 000000000..29cc68ab0 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle6.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle6 : SubtitleFormat + { + private static readonly Regex RegexBeforeText = new Regex(@"^\d\s+\d\s+\d\s+\d\s+\d\s+\d$", RegexOptions.Compiled); + private static readonly Regex RegexTimeCodes = new Regex(@"^\d+\s+\d+$", RegexOptions.Compiled); + + private enum ExpectingLine + { + TimeCodes, + BeforeText, + Text + } + + public override string Extension + { + get { return ".titl"; } + } + + public override string Name + { + get { return "Unknown 6"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + + sb.Append(' '); + sb.Append(subtitle.Paragraphs.Count); + sb.AppendLine(" 4 1234 "); + sb.AppendLine(@"NORMAL +00:00:00.00 + +SRPSKI + +00:00:00.00 +26.11.2008 18:54:15"); + + foreach (Paragraph p in subtitle.Paragraphs) + { + string firstLine = string.Empty; + string secondLine = string.Empty; + var lines = p.Text.SplitToLines(); + if (lines.Length > 2) + { + lines = Utilities.AutoBreakLine(p.Text).SplitToLines(); + } + if (lines.Length > 0) + firstLine = lines[0]; + if (lines.Length > 1) + secondLine = lines[1]; + + sb.AppendLine(string.Format(" {0} {1} " + Environment.NewLine + + "1 0 0 0 0 0" + Environment.NewLine + + "{2}" + Environment.NewLine + + "{3}", p.StartTime.TotalMilliseconds / 10, p.EndTime.TotalMilliseconds / 10, firstLine, secondLine)); + } + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + var paragraph = new Paragraph(); + ExpectingLine expecting = ExpectingLine.TimeCodes; + _errorCount = 0; + + subtitle.Paragraphs.Clear(); + foreach (string line in lines) + { + string s = line.Trim(); + if (RegexTimeCodes.IsMatch(s)) + { + if (!string.IsNullOrEmpty(paragraph.Text)) + subtitle.Paragraphs.Add(paragraph); + + paragraph = new Paragraph(); + string[] parts = s.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 2) + { + try + { + paragraph.StartTime.TotalMilliseconds = long.Parse(parts[0]) * 10; + paragraph.EndTime.TotalMilliseconds = long.Parse(parts[1]) * 10; + expecting = ExpectingLine.BeforeText; + } + catch + { + expecting = ExpectingLine.TimeCodes; + } + } + } + else if (RegexBeforeText.IsMatch(s)) + { + expecting = ExpectingLine.Text; + } + else + { + if (expecting == ExpectingLine.Text) + { + if (s.Length > 0) + { + if (!string.IsNullOrEmpty(paragraph.Text)) + paragraph.Text += Environment.NewLine + s; + else + paragraph.Text = s; + } + } + } + } + if (!string.IsNullOrEmpty(paragraph.Text)) + subtitle.Paragraphs.Add(paragraph); + subtitle.Renumber(); + } + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/UnknownSubtitle60.cs b/libse/SubtitleFormats/UnknownSubtitle60.cs new file mode 100644 index 000000000..1ed16ef14 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle60.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle60 : SubtitleFormat + { + //01:00:31:14 + //THE PRIME MINISTER + //Thank you. + + //01:00:32:06 + //STIG + //But first we'll go to our foreign guest, welcome to the programme. It’s a great pleasure having you here. Let me start with a standard question; is this your first time in Sweden? + + //01:00:44:16 + //FEMALE ARTIST + //No, I was here once many years ago, and I was introduced to your ”surströmming” – is that..? + + private static readonly Regex RegexTimeCodes1 = new Regex(@"^\d\d:\d\d:\d\d:\d\d$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 60"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + + var sb = new StringBuilder(); + foreach (string line in lines) + sb.AppendLine(line); + + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.AppendLine(EncodeTimeCode(p.StartTime)); + sb.AppendLine(EncodeTimeCode(p.EndTime)); + if (!string.IsNullOrEmpty(p.Actor)) + sb.AppendLine(p.Actor.ToUpper()); + else + sb.AppendLine("UNKNOWN ACTOR"); + sb.AppendLine(HtmlUtil.RemoveHtmlTags(p.Text)); + sb.AppendLine(); + } + return sb.ToString(); + } + + private static string EncodeTimeCode(TimeCode time) + { + return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", time.Hours, time.Minutes, time.Seconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + bool expectStartTime = true; + bool expectActor = false; + var p = new Paragraph(); + subtitle.Paragraphs.Clear(); + foreach (string line in lines) + { + string s = line.Trim(); + var match = RegexTimeCodes1.Match(s); + if (match.Success && s.Length == 11) + { + if (p.StartTime.TotalMilliseconds > 0) + { + subtitle.Paragraphs.Add(p); + if (string.IsNullOrEmpty(p.Text)) + _errorCount++; + } + + p = new Paragraph(); + string[] parts = s.Split(':'); + if (parts.Length == 4) + { + try + { + p.StartTime = DecodeTimeCode(parts); + expectActor = true; + expectStartTime = false; + } + catch (Exception exception) + { + _errorCount++; + System.Diagnostics.Debug.WriteLine(exception.Message); + expectStartTime = true; + } + } + } + else if (!string.IsNullOrWhiteSpace(line) && expectActor) + { + if (line == line.ToUpper()) + p.Actor = line; + else + _errorCount++; + expectActor = false; + } + else if (!string.IsNullOrWhiteSpace(line) && !expectActor && !expectStartTime) + { + p.Text = (p.Text + Environment.NewLine + line).Trim(); + if (p.Text.Length > 5000) + { + _errorCount += 10; + return; + } + } + } + if (p.StartTime.TotalMilliseconds > 0) + subtitle.Paragraphs.Add(p); + + bool allNullEndTime = true; + for (int i = 0; i < subtitle.Paragraphs.Count; i++) + { + p = subtitle.Paragraphs[i]; + if (p.EndTime.TotalMilliseconds != 0) + allNullEndTime = false; + + p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + Utilities.GetOptimalDisplayMilliseconds(p.Text); + if (i < subtitle.Paragraphs.Count - 2 && p.EndTime.TotalMilliseconds >= subtitle.Paragraphs[i + 1].StartTime.TotalMilliseconds) + p.EndTime.TotalMilliseconds = subtitle.Paragraphs[i + 1].StartTime.TotalMilliseconds - Configuration.Settings.General.MinimumMillisecondsBetweenLines; + } + if (!allNullEndTime) + subtitle.Paragraphs.Clear(); + + subtitle.RemoveEmptyLines(); + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + //00:00:07:12 + string hour = parts[0]; + string minutes = parts[1]; + string seconds = parts[2]; + string frames = parts[3]; + + return new TimeCode(int.Parse(hour), int.Parse(minutes), int.Parse(seconds), FramesToMillisecondsMax999(int.Parse(frames))); + } + + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle61.cs b/libse/SubtitleFormats/UnknownSubtitle61.cs new file mode 100644 index 000000000..0af431bd5 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle61.cs @@ -0,0 +1,155 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle61 : SubtitleFormat + { + //00:02:23.59 + //קרוליין: פשוט תשימי את זה בפייסבוק או משהו. + + //00:02:25.78 + //הם בטוח יאהבו את זה. + + //00:02:27.78 + //ליזי: אוקיי. אז אני מניחה שזה זמן הנתינה + private static readonly Regex RegexTimeCodes1 = new Regex(@"^\d\d:\d\d:\d\d\.\d\d$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 61"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + + var sb = new StringBuilder(); + foreach (string line in lines) + sb.AppendLine(line); + + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.AppendLine(EncodeTimeCode(p.StartTime)); + sb.AppendLine(EncodeTimeCode(p.EndTime)); + sb.AppendLine(HtmlUtil.RemoveHtmlTags(p.Text)); + sb.AppendLine(); + } + return sb.ToString(); + } + + private static string EncodeTimeCode(TimeCode time) + { + return string.Format("{0:00}:{1:00}:{2:00}.{3:00}", time.Hours, time.Minutes, time.Seconds, time.Milliseconds / 10); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + bool expectStartTime = true; + var p = new Paragraph(); + subtitle.Paragraphs.Clear(); + foreach (string line in lines) + { + string s = line.Trim(); + var match = RegexTimeCodes1.Match(s); + if (match.Success && s.Length == 11) + { + if (!expectStartTime) + _errorCount++; + + if (p.StartTime.TotalMilliseconds > 0) + { + subtitle.Paragraphs.Add(p); + if (string.IsNullOrEmpty(p.Text)) + _errorCount++; + } + + p = new Paragraph(); + string[] parts = s.Split(new[] { ':', '.' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 4) + { + try + { + p.StartTime = DecodeTimeCode(parts); + expectStartTime = false; + } + catch (Exception exception) + { + _errorCount++; + System.Diagnostics.Debug.WriteLine(exception.Message); + expectStartTime = true; + } + } + } + else if (string.IsNullOrWhiteSpace(line)) + { + expectStartTime = true; + } + else if (!expectStartTime) + { + p.Text = (p.Text + Environment.NewLine + line).Trim(); + if (p.Text.Length > 5000) + { + _errorCount += 10; + return; + } + } + else + { + _errorCount++; + } + } + if (p.StartTime.TotalMilliseconds > 0) + subtitle.Paragraphs.Add(p); + + bool allNullEndTime = true; + for (int i = 0; i < subtitle.Paragraphs.Count; i++) + { + p = subtitle.Paragraphs[i]; + if (p.EndTime.TotalMilliseconds != 0) + allNullEndTime = false; + + p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + Utilities.GetOptimalDisplayMilliseconds(p.Text); + if (i < subtitle.Paragraphs.Count - 2 && p.EndTime.TotalMilliseconds >= subtitle.Paragraphs[i + 1].StartTime.TotalMilliseconds) + p.EndTime.TotalMilliseconds = subtitle.Paragraphs[i + 1].StartTime.TotalMilliseconds - Configuration.Settings.General.MinimumMillisecondsBetweenLines; + } + if (!allNullEndTime) + subtitle.Paragraphs.Clear(); + + subtitle.RemoveEmptyLines(); + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + //00:00:07:12 + string hour = parts[0]; + string minutes = parts[1]; + string seconds = parts[2]; + string msDiv10 = parts[3]; + + return new TimeCode(int.Parse(hour), int.Parse(minutes), int.Parse(seconds), int.Parse(msDiv10) * 10); + } + + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle62.cs b/libse/SubtitleFormats/UnknownSubtitle62.cs new file mode 100644 index 000000000..6b4016db0 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle62.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle62 : SubtitleFormat + { + + // 338: 00:24:34.00 00:24:37.10 [51] + private static readonly Regex RegexTimeCodes = new Regex(@"^\d+:\s+\d\d:\d\d:\d\d\.\d\d\s+\d\d:\d\d:\d\d\.\d\d\s+\[\d+\]$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 62"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + + var sb = new StringBuilder(); + foreach (string line in lines) + sb.AppendLine(line); + + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string format = "{0}: {1} {2} [{3}]"; + var sb = new StringBuilder(); + int count = 1; + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.AppendLine(string.Format(format, count, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), p.Text.Length)); + sb.AppendLine(p.Text); + sb.AppendLine(); + count++; + } + return sb.ToString().Trim(); + } + + private static string EncodeTimeCode(TimeCode time) + { + return string.Format("{0:00}:{1:00}:{2:00}.{3:00}", time.Hours, time.Minutes, time.Seconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + bool expectStartTime = true; + var p = new Paragraph(); + subtitle.Paragraphs.Clear(); + foreach (string line in lines) + { + string s = line.Trim().Replace("*", string.Empty); + var match = RegexTimeCodes.Match(s); + if (match.Success) + { + string[] parts = s.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 4) + { + try + { + if (!string.IsNullOrEmpty(p.Text)) + { + subtitle.Paragraphs.Add(p); + p = new Paragraph(); + } + p.StartTime = DecodeTimeCode(parts[1]); + p.EndTime = DecodeTimeCode(parts[2]); + expectStartTime = false; + } + catch (Exception exception) + { + _errorCount++; + System.Diagnostics.Debug.WriteLine(exception.Message); + } + } + } + else if (string.IsNullOrWhiteSpace(line)) + { + if (p.StartTime.TotalMilliseconds == 0 && p.EndTime.TotalMilliseconds == 0) + _errorCount++; + else + subtitle.Paragraphs.Add(p); + p = new Paragraph(); + } + else if (!expectStartTime) + { + p.Text = (p.Text + Environment.NewLine + line).Trim(); + if (p.Text.Length > 500) + { + _errorCount += 10; + return; + } + while (p.Text.Contains(Environment.NewLine + " ")) + p.Text = p.Text.Replace(Environment.NewLine + " ", Environment.NewLine); + } + } + if (!string.IsNullOrEmpty(p.Text)) + subtitle.Paragraphs.Add(p); + + subtitle.RemoveEmptyLines(); + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string part) + { + string[] parts = part.Split(new[] { '.', ':' }, StringSplitOptions.RemoveEmptyEntries); + + //00:00:07:12 + string hour = parts[0]; + string minutes = parts[1]; + string seconds = parts[2]; + string frames = parts[3]; + + return new TimeCode(int.Parse(hour), int.Parse(minutes), int.Parse(seconds), FramesToMillisecondsMax999(int.Parse(frames))); + } + + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle63.cs b/libse/SubtitleFormats/UnknownSubtitle63.cs new file mode 100644 index 000000000..56e97454a --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle63.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle63 : SubtitleFormat + { + + //3: 00:00:09:23 00:00:16:21 06:23 + //Alustame sellest... + //Siin kajab kuidagi harjumatult. + private static readonly Regex RegexTimeCodes = new Regex(@"^\d+:\s+\d\d:\d\d:\d\d\:\d\d \d\d:\d\d:\d\d\:\d\d \d\d:\d\d$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 63"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + + var sb = new StringBuilder(); + foreach (string line in lines) + sb.AppendLine(line); + + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string format = "{0}: {1} {2} {3:00}:{4:00}"; + var sb = new StringBuilder(); + int count = 1; + foreach (Paragraph p in subtitle.Paragraphs) + { + // to avoid rounding errors in duration + var startFrame = MillisecondsToFramesMaxFrameRate(p.StartTime.Milliseconds); + var endFrame = MillisecondsToFramesMaxFrameRate(p.EndTime.Milliseconds); + var durationCalc = new Paragraph( + new TimeCode(p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, FramesToMillisecondsMax999(startFrame)), + new TimeCode(p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, FramesToMillisecondsMax999(endFrame)), + string.Empty); + + sb.AppendLine(string.Format(format, count, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), durationCalc.Duration.Seconds, MillisecondsToFramesMaxFrameRate(durationCalc.Duration.Milliseconds))); + sb.AppendLine(p.Text); + sb.AppendLine(); + count++; + } + return sb.ToString().Trim(); + } + + private static string EncodeTimeCode(TimeCode time) + { + return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", time.Hours, time.Minutes, time.Seconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + bool expectStartTime = true; + var p = new Paragraph(); + subtitle.Paragraphs.Clear(); + foreach (string line in lines) + { + string s = line.Trim().Replace("*", string.Empty); + var match = RegexTimeCodes.Match(s); + if (match.Success) + { + string[] parts = s.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 4) + { + try + { + if (!string.IsNullOrEmpty(p.Text)) + { + subtitle.Paragraphs.Add(p); + p = new Paragraph(); + } + p.StartTime = DecodeTimeCode(parts[1]); + p.EndTime = DecodeTimeCode(parts[2]); + expectStartTime = false; + } + catch (Exception exception) + { + _errorCount++; + System.Diagnostics.Debug.WriteLine(exception.Message); + } + } + } + else if (string.IsNullOrWhiteSpace(line)) + { + if (p.StartTime.TotalMilliseconds == 0 && p.EndTime.TotalMilliseconds == 0) + _errorCount++; + else + subtitle.Paragraphs.Add(p); + p = new Paragraph(); + } + else if (!expectStartTime) + { + p.Text = (p.Text + Environment.NewLine + line).Trim(); + if (p.Text.Length > 500) + { + _errorCount += 10; + return; + } + while (p.Text.Contains(Environment.NewLine + " ")) + p.Text = p.Text.Replace(Environment.NewLine + " ", Environment.NewLine); + } + } + if (!string.IsNullOrEmpty(p.Text)) + subtitle.Paragraphs.Add(p); + + subtitle.RemoveEmptyLines(); + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string part) + { + string[] parts = part.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + + string hour = parts[0]; + string minutes = parts[1]; + string seconds = parts[2]; + string frames = parts[3]; + + return new TimeCode(int.Parse(hour), int.Parse(minutes), int.Parse(seconds), FramesToMillisecondsMax999(int.Parse(frames))); + } + + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle64.cs b/libse/SubtitleFormats/UnknownSubtitle64.cs new file mode 100644 index 000000000..ada266e94 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle64.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle64 : SubtitleFormat + { + + private static readonly Regex RegexTimeCode = new Regex(@"^\d+:\d\d:\d\d:\d\d$", RegexOptions.Compiled); + + private enum ExpectingLine + { + Number, + TimeStart, + TimeEnd, + Text + } + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 64"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + // 1 + // 00:00:04:12 + // 00:00:06:05 + // Berniukai, tik pažiūrėkit. + + // 2 + // 00:00:06:16 + // 00:00:07:20 + // Argi ne puiku? + + // 3 + // 00:00:08:02 + // 00:00:10:20 + // Tėti, ar galime čia paplaukioti? + // -Aišku, kad galim. + + const string paragraphWriteFormat = "{4}{3}{0}{3}{1}{3}{2}{3}"; + var sb = new StringBuilder(); + int count = 0; + foreach (Paragraph p in subtitle.Paragraphs) + { + count++; + var text = HtmlUtil.RemoveOpenCloseTags(p.Text, HtmlUtil.TagFont); + sb.AppendLine(string.Format(paragraphWriteFormat, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), text, Environment.NewLine, count)); + } + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + Paragraph paragraph = null; + ExpectingLine expecting = ExpectingLine.Number; + _errorCount = 0; + + subtitle.Paragraphs.Clear(); + foreach (string line in lines) + { + if (line.Length < 6 && Utilities.IsInteger(line)) + { + if (paragraph != null) + subtitle.Paragraphs.Add(paragraph); + paragraph = new Paragraph(); + expecting = ExpectingLine.TimeStart; + } + else if (paragraph != null && expecting == ExpectingLine.TimeStart && RegexTimeCode.IsMatch(line)) + { + string[] parts = line.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 4) + { + try + { + var tc = DecodeTimeCode(parts); + paragraph.StartTime = tc; + expecting = ExpectingLine.TimeEnd; + } + catch + { + _errorCount++; + expecting = ExpectingLine.Number; + } + } + } + else if (paragraph != null && expecting == ExpectingLine.TimeEnd && RegexTimeCode.IsMatch(line)) + { + string[] parts = line.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 4) + { + try + { + var tc = DecodeTimeCode(parts); + paragraph.EndTime = tc; + expecting = ExpectingLine.Text; + } + catch + { + _errorCount++; + expecting = ExpectingLine.Number; + } + } + } + else + { + if (paragraph != null && expecting == ExpectingLine.Text) + { + if (line.Length > 0) + { + string s = line; + paragraph.Text = (paragraph.Text + Environment.NewLine + s).Trim(); + if (paragraph.Text.Length > 2000) + { + _errorCount += 100; + return; + } + } + } + else + { + _errorCount++; + } + } + } + if (paragraph != null && !string.IsNullOrEmpty(paragraph.Text)) + subtitle.Paragraphs.Add(paragraph); + subtitle.Renumber(); + } + + private static string EncodeTimeCode(TimeCode time) + { + return string.Format("{0}:{1:00}:{2:00}:{3:00}", time.Hours, time.Minutes, time.Seconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + string hour = parts[0]; + string minutes = parts[1]; + string seconds = parts[2]; + string frames = parts[3]; + + return new TimeCode(int.Parse(hour), int.Parse(minutes), int.Parse(seconds), FramesToMillisecondsMax999(int.Parse(frames))); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/UnknownSubtitle65.cs b/libse/SubtitleFormats/UnknownSubtitle65.cs new file mode 100644 index 000000000..0816be0ee --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle65.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle65 : SubtitleFormat + { + private enum ExpectingLine + { + TimeCodes, + Text + } + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 65"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string paragraphWriteFormat = "{0:00}:{1:00}:{2:00},{3:00}:{4:00}:{5:00}{6}{7}"; + + //00:00:08,00:00:13 + //The 8.7 update will bring the British self-propelled guns, the map, called Severogorsk, + + //00:00:13,00:00:18 + //the soviet light tank MT-25 and the new German premium TD, the E25. + + //00:00:18,00:00:22 + //We will tell you about this and lots of other things in our review. + + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + string text = p.Text.Replace(Environment.NewLine, " "); + + sb.AppendLine(string.Format(paragraphWriteFormat, + p.StartTime.Hours, + p.StartTime.Minutes, + RoundSeconds(p.StartTime), + p.EndTime.Hours, + p.EndTime.Minutes, + RoundSeconds(p.EndTime), + Environment.NewLine, + text)); + sb.AppendLine(); + } + return sb.ToString().Trim(); + } + + private static int RoundSeconds(TimeCode tc) + { + int rounded = (int)Math.Round(tc.Seconds + tc.Milliseconds / TimeCode.BaseUnit); + return rounded; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + var regexTimeCodes = new Regex(@"^\d\d:\d\d:\d\d,\d\d:\d\d:\d\d$", RegexOptions.Compiled); + + var paragraph = new Paragraph(); + ExpectingLine expecting = ExpectingLine.TimeCodes; + _errorCount = 0; + + subtitle.Paragraphs.Clear(); + foreach (string line in lines) + { + if (regexTimeCodes.IsMatch(line)) + { + string[] parts = line.Split(new[] { ':', ',', '.', ' ' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 6) + { + try + { + int startHours = int.Parse(parts[0]); + int startMinutes = int.Parse(parts[1]); + int startSeconds = int.Parse(parts[2]); + int endHours = int.Parse(parts[3]); + int endMinutes = int.Parse(parts[4]); + int endSeconds = int.Parse(parts[5]); + paragraph.StartTime = new TimeCode(startHours, startMinutes, startSeconds, 0); + paragraph.EndTime = new TimeCode(endHours, endMinutes, endSeconds, 0); + expecting = ExpectingLine.Text; + } + catch + { + expecting = ExpectingLine.TimeCodes; + } + } + } + else + { + if (expecting == ExpectingLine.Text) + { + if (line.Length > 0) + { + string text = Utilities.AutoBreakLine(line.Trim()); + paragraph.Text = text; + subtitle.Paragraphs.Add(paragraph); + paragraph = new Paragraph(); + expecting = ExpectingLine.TimeCodes; + } + } + } + } + subtitle.Renumber(); + } + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle66.cs b/libse/SubtitleFormats/UnknownSubtitle66.cs new file mode 100644 index 000000000..30ebb10dc --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle66.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle66 : SubtitleFormat + { + + // 24 10:08:57:17 10:08:59:15 01:23 + //The question is, + // + // 25 10:08:59:19 10:09:04:01 04:07 + //is this upside-down vision + //permanent or only temporary? + private static readonly Regex RegexTimeCodes = new Regex(@"^\d+\s+\d\d:\d\d:\d\d\:\d\d\s+\d\d:\d\d:\d\d\:\d\d\s+\d\d:\d\d$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 66"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + + var sb = new StringBuilder(); + foreach (string line in lines) + sb.AppendLine(line); + + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string format = "{0} {1} {2} {3:00}:{4:00}"; + var sb = new StringBuilder(); + int count = 1; + foreach (Paragraph p in subtitle.Paragraphs) + { + // to avoid rounding errors in duration + var startFrame = MillisecondsToFramesMaxFrameRate(p.StartTime.Milliseconds); + var endFrame = MillisecondsToFramesMaxFrameRate(p.EndTime.Milliseconds); + var durationCalc = new Paragraph( + new TimeCode(p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, FramesToMillisecondsMax999(startFrame)), + new TimeCode(p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, FramesToMillisecondsMax999(endFrame)), + string.Empty); + + sb.AppendLine(string.Format(format, count.ToString(CultureInfo.InvariantCulture).PadLeft(5), EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), durationCalc.Duration.Seconds, MillisecondsToFramesMaxFrameRate(durationCalc.Duration.Milliseconds))); + sb.AppendLine(p.Text); + sb.AppendLine(); + count++; + } + return sb.ToString().TrimEnd(); + } + + private static string EncodeTimeCode(TimeCode time) + { + return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", time.Hours, time.Minutes, time.Seconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + bool expectStartTime = true; + var p = new Paragraph(); + subtitle.Paragraphs.Clear(); + foreach (string line in lines) + { + string s = line.Trim().Replace("*", string.Empty); + var match = RegexTimeCodes.Match(s); + if (match.Success) + { + string[] parts = s.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 4) + { + try + { + if (!string.IsNullOrEmpty(p.Text)) + { + subtitle.Paragraphs.Add(p); + p = new Paragraph(); + } + p.StartTime = DecodeTimeCode(parts[1]); + p.EndTime = DecodeTimeCode(parts[2]); + expectStartTime = false; + } + catch (Exception exception) + { + _errorCount++; + System.Diagnostics.Debug.WriteLine(exception.Message); + } + } + } + else if (string.IsNullOrWhiteSpace(line)) + { + if (p.StartTime.TotalMilliseconds == 0 && p.EndTime.TotalMilliseconds == 0) + _errorCount++; + else + subtitle.Paragraphs.Add(p); + p = new Paragraph(); + } + else if (!expectStartTime) + { + p.Text = (p.Text + Environment.NewLine + line).Trim(); + if (p.Text.Length > 500) + { + _errorCount += 10; + return; + } + while (p.Text.Contains(Environment.NewLine + " ")) + p.Text = p.Text.Replace(Environment.NewLine + " ", Environment.NewLine); + } + } + if (!string.IsNullOrEmpty(p.Text)) + subtitle.Paragraphs.Add(p); + + subtitle.RemoveEmptyLines(); + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string part) + { + string[] parts = part.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + + string hour = parts[0]; + string minutes = parts[1]; + string seconds = parts[2]; + string frames = parts[3]; + + return new TimeCode(int.Parse(hour), int.Parse(minutes), int.Parse(seconds), FramesToMillisecondsMax999(int.Parse(frames))); + } + + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle67.cs b/libse/SubtitleFormats/UnknownSubtitle67.cs new file mode 100644 index 000000000..93a6fb3af --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle67.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle67 : SubtitleFormat + { + public override string Extension + { + get { return ".xml"; } + } + + public override string Name + { + get { return "Unknown 67"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > 0; + } + + public override string ToText(Subtitle subtitle, string title) + { + string xmlStructure = + "" + Environment.NewLine + + ""; + + var xml = new XmlDocument(); + xml.LoadXml(xmlStructure); + + foreach (Paragraph p in subtitle.Paragraphs) + { + XmlNode paragraph = xml.CreateElement("Cue"); + + XmlAttribute start = xml.CreateAttribute("value"); + start.InnerText = ((long)(Math.Round(p.StartTime.TotalMilliseconds))).ToString(CultureInfo.InvariantCulture); + paragraph.Attributes.Append(start); + + XmlAttribute duration = xml.CreateAttribute("lineBreakBefore"); + duration.InnerText = "true"; + paragraph.Attributes.Append(duration); + + paragraph.InnerText = p.Text; + + xml.DocumentElement.AppendChild(paragraph); + } + + return ToUtf8XmlString(xml); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + + string allText = sb.ToString(); + if (!allText.Contains(" next.StartTime.TotalMilliseconds) + p.EndTime.TotalMilliseconds = next.StartTime.TotalMilliseconds - Configuration.Settings.General.MinimumMillisecondsBetweenLines; + } + } + subtitle.Renumber(); + } + + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle68.cs b/libse/SubtitleFormats/UnknownSubtitle68.cs new file mode 100644 index 000000000..01b8ed763 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle68.cs @@ -0,0 +1,191 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle68 : SubtitleFormat + { + private static readonly Regex RegexTimeCode = new Regex(@"^\d\d:\d\d:\d\dF\d\d", RegexOptions.Compiled); //10:00:02F00 + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 68"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + //Mais l'image utilisée pour + //nous vendre la nourriture + // 10:00:44F11 + // + //est restée celle d'une Amérique + //bucolique et agraire. + // 10:00:46F18 10:00:50F14 + var sb = new StringBuilder(); + if (subtitle.Header != null && subtitle.Header.Contains(";» DO NOT MODIFY THE FOLLOWING 3 LINES")) + { + sb.AppendLine(subtitle.Header.Trim()); + sb.AppendLine(); + } + else + { + sb.AppendLine(@";» Video file: C:\Documents and Settings\video.mpg +;» Last edited: 24 sept. 09 11:26 +;» Timing model: NTSC (30 fps) +;» Drop frame timing: ON +;» Number of captions: 1348 +;» Caption time codes: 10:00:02F00 - 11:32:07F00 +;» Video start time: 09:59:15F12 (Forced) +;» Insertion disk created: NO +;» Reading speed: 300 +;» Minimum display time: 30 +;» Maximum display time (sec.): 5 +;» Minimum erase time: 0 +;» Tab stop value: 4 +;» Sticky mode: OFF +;» Right justification ragged left: OFF +;» Parallelogram filter: OFF +;» Coding standard: EIA-608" + Environment.NewLine + +";» \"Bottom\" row: 15" + @" +;» Lines per caption: 15 +;» Characters per line: 32 +;» Default horizontal position: Center +;» Default vertical position: Bottom +;» Default mode: PopOn +;» Captioning channel: 1 + +;» DO NOT MODIFY THE FOLLOWING 3 LINES +;»»10<210>10000001000000040?000?000100030000100?:?8;;:400685701001000100210<1509274 +;»»2000000000200500005>??????091000000014279616<6000000000000000000000000000000000000000000000000000000005000105=4641;? +;»»0020??000000900<0<0<008000000000000<0<0<0080000000??00<0??000=200>1000;5=83<:; +;» +;» ************************************************************************"); + sb.AppendLine(); + } + + const string paragraphWriteFormat = "{0}{1} {2} {3}{1}"; + foreach (Paragraph p in subtitle.Paragraphs) + { + string text = p.Text.Replace("♪", "|"); + if (text.StartsWith("")) + text = ",b" + Environment.NewLine + text; + if (text.StartsWith("{\\an8}")) + text = ",12" + Environment.NewLine + text; + text = HtmlUtil.RemoveHtmlTags(text, true); + sb.AppendLine(string.Format(paragraphWriteFormat, text, Environment.NewLine, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime))); + } + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + subtitle.Paragraphs.Clear(); + var text = new StringBuilder(); + var header = new StringBuilder(); + Paragraph p = null; + for (int i = 0; i < lines.Count; i++) + { + string line = lines[i].Trim(); + if (subtitle.Paragraphs.Count == 0 && line.StartsWith(';') || line.Length == 0) + { + header.AppendLine(line); + } + else if (RegexTimeCode.IsMatch(line)) + { + var timeParts = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + if (timeParts.Length == 1) + { + try + { + TimeCode start = DecodeTimeCode(timeParts[0]); + if (p != null && p.EndTime.TotalMilliseconds == 0) + p.EndTime.TotalMilliseconds = start.TotalMilliseconds - Configuration.Settings.General.MinimumMillisecondsBetweenLines; + TimeCode end = new TimeCode(0, 0, 0, 0); + p = MakeTextParagraph(text, p, start, end); + subtitle.Paragraphs.Add(p); + text = new StringBuilder(); + } + catch + { + _errorCount++; + } + } + else if (timeParts.Length == 2) + { + try + { + TimeCode start = DecodeTimeCode(timeParts[0]); + if (p != null && p.EndTime.TotalMilliseconds == 0) + p.EndTime.TotalMilliseconds = start.TotalMilliseconds - Configuration.Settings.General.MinimumMillisecondsBetweenLines; + TimeCode end = DecodeTimeCode(timeParts[1]); + p = MakeTextParagraph(text, p, start, end); + subtitle.Paragraphs.Add(p); + text = new StringBuilder(); + } + catch + { + _errorCount++; + } + } + } + else if (!string.IsNullOrWhiteSpace(line)) + { + text.AppendLine(line.Trim().Replace("|", "♪")); + if (text.Length > 5000) + return; + } + else + { + text = new StringBuilder(); + } + } + subtitle.Header = header.ToString(); + subtitle.Renumber(); + } + + private static Paragraph MakeTextParagraph(StringBuilder text, Paragraph p, TimeCode start, TimeCode end) + { + p = new Paragraph(start, end, text.ToString().Trim()); + if (p.Text.StartsWith(",b" + Environment.NewLine)) + p.Text = "" + p.Text.Remove(0, 2).Trim() + ""; + else if (p.Text.StartsWith(",1" + Environment.NewLine)) + p.Text = "{\\an8}" + p.Text.Remove(0, 2).Trim(); + else if (p.Text.StartsWith(",12" + Environment.NewLine)) + p.Text = "{\\an8}" + p.Text.Remove(0, 3).Trim(); + return p; + } + + private static string EncodeTimeCode(TimeCode time) + { + return string.Format("{0:00}:{1:00}:{2:00}F{3:00}", time.Hours, time.Minutes, time.Seconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); + } + + private static TimeCode DecodeTimeCode(string timePart) + { + string s = timePart.Substring(0, 11); + var parts = s.Split(new[] { ':', 'F' }, StringSplitOptions.RemoveEmptyEntries); + return new TimeCode(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), FramesToMillisecondsMax999(int.Parse(parts[3]))); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/UnknownSubtitle69.cs b/libse/SubtitleFormats/UnknownSubtitle69.cs new file mode 100644 index 000000000..973adfb95 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle69.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle69 : SubtitleFormat + { + private static readonly Regex RegexTimeCode = new Regex(@"^\d+\) \d\d:\d\d:\d\d:\d\d \d\d:\d\d:\d\d:\d\d Durée : \d\d:\d\d", RegexOptions.Compiled); //10:00:02F00 + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 69"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + //1) 00:00:06:14 00:00:07:07 Durée : 00:18 Lisibilité : 011 Intervalle : 06:14 Nbc : 018 + //text + //line2 + + //2) 00:00:07:14 00:00:09:02 Durée : 01:13 Lisibilité : 023 Intervalle : 00:07 Nbc : 026 + //text + var sb = new StringBuilder(); + string paragraphWriteFormat = "{0}) {1} {2} Durée : {3} Lisibilité : {4} Intervalle : {5} Nbc : {6}" + Environment.NewLine + "{7}"; + int count = 1; + foreach (Paragraph p in subtitle.Paragraphs) + { + string text = HtmlUtil.RemoveHtmlTags(p.Text, true); + string start = p.StartTime.ToHHMMSSFF(); + string end = p.EndTime.ToHHMMSSFF(); + string duration = string.Format("{0:00}:{1:00}", p.Duration.Seconds, MillisecondsToFramesMaxFrameRate(p.Duration.Milliseconds)); + const string readability = "011"; + const string interval = "06:14"; + string nbc = text.Length.ToString().PadLeft(3, '0'); + sb.AppendLine(string.Format(paragraphWriteFormat, count, start, end, duration, readability, interval, nbc, text)); + sb.AppendLine(); + count++; + } + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + subtitle.Paragraphs.Clear(); + var text = new StringBuilder(); + Paragraph p = null; + for (int i = 0; i < lines.Count; i++) + { + string line = lines[i].Trim(); + if (line.Length == 0) + { + if (p != null) + p.Text = text.ToString().Trim(); + } + else if (RegexTimeCode.IsMatch(line)) + { + var timeParts = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + if (timeParts.Length > 4) + { + try + { + string start = timeParts[1]; + string end = timeParts[2]; + p = new Paragraph(); + p.StartTime = DecodeTimeCode(start); + p.EndTime = DecodeTimeCode(end); + subtitle.Paragraphs.Add(p); + text = new StringBuilder(); + } + catch + { + _errorCount++; + } + } + } + else + { + text.AppendLine(line); + if (text.Length > 5000) + return; + } + } + if (p != null) + p.Text = text.ToString().Trim(); + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string timePart) + { + string s = timePart.Substring(0, 11); + var parts = s.Split(new[] { ':', 'F' }, StringSplitOptions.RemoveEmptyEntries); + return new TimeCode(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), FramesToMillisecondsMax999(int.Parse(parts[3]))); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/UnknownSubtitle7.cs b/libse/SubtitleFormats/UnknownSubtitle7.cs new file mode 100644 index 000000000..c5b16ed48 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle7.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + /// + /// Reported by dipa nuswantara + /// + public class UnknownSubtitle7 : SubtitleFormat + { + private enum ExpectingLine + { + TimeStart, + Text, + TimeEndOrText, + } + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 7"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + //00:00:54:16 Bisakah kalian diam,Tolong! + //00:00:56:07 + //00:00:57:16 Benar, tepatnya saya tidak memiliki "Anda + //sudah mendapat 24 jam" adegan... tapi + //00:01:02:03 + + const string paragraphWriteFormat = "{0} {2}{3}{1}\t"; + + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.AppendLine(string.Format(paragraphWriteFormat, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), p.Text, Environment.NewLine)); + } + return sb.ToString(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + var regexTimeCode = new Regex(@"^\d\d:\d\d:\d\d:\d\d ", RegexOptions.Compiled); + var regexTimeCodeEnd = new Regex(@"^\d\d:\d\d:\d\d:\d\d\t$", RegexOptions.Compiled); + + var paragraph = new Paragraph(); + var expecting = ExpectingLine.TimeStart; + _errorCount = 0; + + subtitle.Paragraphs.Clear(); + int count = 0; + foreach (string line in lines) + { + count++; + if (regexTimeCode.IsMatch(line)) + { + string[] parts = line.Substring(0, 11).Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 4) + { + try + { + var tc = DecodeTimeCode(parts); + if (expecting == ExpectingLine.TimeStart) + { + paragraph = new Paragraph(); + paragraph.StartTime = tc; + expecting = ExpectingLine.Text; + + if (line.Length > 12) + { + paragraph.Text = line.Substring(12).Trim(); + } + } + } + catch + { + _errorCount++; + expecting = ExpectingLine.TimeStart; + } + } + } + else if (regexTimeCodeEnd.IsMatch(line) || (count == lines.Count && regexTimeCodeEnd.IsMatch(line + "\t"))) + { + string[] parts = line.Substring(0, 11).Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 4) + { + var tc = DecodeTimeCode(parts); + paragraph.EndTime = tc; + subtitle.Paragraphs.Add(paragraph); + paragraph = new Paragraph(); + expecting = ExpectingLine.TimeStart; + } + } + else + { + if (expecting == ExpectingLine.Text) + { + if (line.Length > 0) + { + string text = line.Replace("|", Environment.NewLine); + paragraph.Text += Environment.NewLine + text; + expecting = ExpectingLine.TimeEndOrText; + + if (paragraph.Text.Length > 2000) + { + _errorCount += 100; + return; + } + } + } + else + { + _errorCount++; + } + } + } + subtitle.Renumber(); + } + + private static string EncodeTimeCode(TimeCode time) + { + return time.ToHHMMSSFF(); + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + var hour = int.Parse(parts[0]); + var minutes = int.Parse(parts[1]); + var seconds = int.Parse(parts[2]); + var frames = int.Parse(parts[3]); + return new TimeCode(hour, minutes, seconds, FramesToMillisecondsMax999(frames)); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/UnknownSubtitle70.cs b/libse/SubtitleFormats/UnknownSubtitle70.cs new file mode 100644 index 000000000..d5a2600cd --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle70.cs @@ -0,0 +1,565 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle70 : SubtitleFormat + { + private static readonly Regex RegexMicroDvdLine = new Regex(@"^\[-?\d+\]\[-?\d+\].*$", RegexOptions.Compiled); + public string Errors { get; private set; } + private StringBuilder _errors; + private int _lineNumber; + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 70"; } + } + + public override bool IsTimeBased + { + get { return false; } + } + + public override bool IsMine(List lines, string fileName) + { + var trimmedLines = new List(); + int errors = 0; + foreach (string line in lines) + { + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } + + if (line.Contains('[')) + { + string s = RemoveIllegalSpacesAndFixEmptyCodes(line); + if (RegexMicroDvdLine.IsMatch(s)) + trimmedLines.Add(s); + else + errors++; + } + else + { + errors++; + } + } + Errors = null; + return trimmedLines.Count > errors; + } + + private static string RemoveIllegalSpacesAndFixEmptyCodes(string line) + { + int index = line.IndexOf(']'); + if (index >= 0 && index < line.Length) + { + index = line.IndexOf(']', index + 1); + if (index >= 0 && index + 1 < line.Length) + { + var indexOfBrackets = line.IndexOf("[]", StringComparison.Ordinal); + if (indexOfBrackets >= 0 && indexOfBrackets < index) + { + line = line.Insert(indexOfBrackets + 1, "0"); // set empty time codes to zero + index++; + } + + while (line.Contains(' ') && line.IndexOf(' ') < index) + { + line = line.Remove(line.IndexOf(' '), 1); + index--; + } + } + } + return line; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.Append('['); + sb.Append(p.StartFrame); + sb.Append("]["); + sb.Append(p.EndFrame); + sb.Append(']'); + + //{y:b} is italics for single line + //{Y:b} is italics for both lines + + var parts = p.Text.SplitToLines(); + int count = 0; + bool italicOn = false; + bool boldOn = false; + bool underlineOn = false; + var lineSb = new StringBuilder(); + foreach (string line in parts) + { + if (count > 0) + lineSb.Append('|'); + + bool alreadyItalic = italicOn; + bool alreadyBold = boldOn; + bool alreadyUnderline = underlineOn; + + var pre = new StringBuilder(); + string s = line; + for (int i = 0; i < 5; i++) + { + if (alreadyItalic && i == 0) + { + italicOn = true; + boldOn = false; + underlineOn = false; + pre.Append("{y:i}"); // italic single line + alreadyItalic = false; + } + else if (s.StartsWith("")) + { + italicOn = true; + boldOn = false; + underlineOn = false; + if (pre.ToString() != "{y:i}") + pre.Append("{y:i}"); // italic single line + s = s.Remove(0, 3); + } + else if (alreadyBold && i == 0) + { + italicOn = false; + boldOn = true; + underlineOn = false; + pre.Append("{y:b}"); // bold single line + } + else if (s.StartsWith("")) + { + italicOn = false; + boldOn = true; + underlineOn = false; + if (pre.ToString() != "{y:b}") + pre.Append("{y:b}"); // bold single line + s = s.Remove(0, 3); + } + else if (alreadyUnderline && i == 0) + { + italicOn = false; + boldOn = false; + underlineOn = true; + pre.Append("{y:u}"); // underline single line + } + else if (s.StartsWith("")) + { + italicOn = false; + boldOn = false; + underlineOn = true; + if (pre.ToString() != "{y:u}") + pre.Append("{y:u}"); // underline single line + s = s.Remove(0, 3); + } + + if (s.StartsWith("'); + if (end > 0) + { + string tag = s.Substring(0, end); + if (tag.Contains(" color=")) + { + int colorStart = tag.IndexOf(" color=", StringComparison.Ordinal); + int colorEnd = tag.IndexOf('"', colorStart + " color=".Length + 1); + if (colorEnd > 0) + { + string color = tag.Substring(colorStart, colorEnd - colorStart); + color = color.Remove(0, " color=".Length); + color = color.Trim('"'); + color = color.Trim('\''); + color = color.TrimStart('#'); + if (color.Length == 6) + { + if (s.Contains(Environment.NewLine) && s.Contains("" + Environment.NewLine)) + pre.Append("{c:$" + color.Substring(4, 2) + color.Substring(2, 2) + color.Substring(0, 2) + "}"); + else + pre.Append("{C:$" + color.Substring(4, 2) + color.Substring(2, 2) + color.Substring(0, 2) + "}"); + } + } + } + if (tag.Contains(" face=")) + { + var faceStart = tag.IndexOf(" face=", StringComparison.Ordinal); + var faceEnd = tag.IndexOf('"', faceStart + " face=".Length + 1); + if (faceEnd > 0) + { + string fontName = tag.Substring(faceStart, faceEnd - faceStart); + fontName = fontName.Remove(0, " face=".Length).Trim(); + fontName = fontName.Trim('"'); + fontName = fontName.Trim('\''); + if (fontName.Length > 0) + { + if (s.Contains(Environment.NewLine) && s.Contains("
" + Environment.NewLine)) + pre.Append("{f:" + fontName + "}"); + else + pre.Append("{F:" + fontName + "}"); + } + } + } + if (tag.Contains(" size=")) + { + var sizeStart = tag.IndexOf(" size=", StringComparison.Ordinal); + var sizeEnd = tag.IndexOf('"', sizeStart + " size=".Length + 1); + if (sizeEnd > 0) + { + string fontSize = tag.Substring(sizeStart, sizeEnd - sizeStart); + fontSize = fontSize.Remove(0, " size=".Length).Trim(); + fontSize = fontSize.Trim('"'); + fontSize = fontSize.Trim('\''); + if (fontSize.Length > 0) + { + if (s.Contains(Environment.NewLine) && s.Contains("
" + Environment.NewLine)) + pre.Append("{s:" + fontSize + "}"); + else + pre.Append("{S:" + fontSize + "}"); + } + } + } + s = s.Remove(0, end + 1); + } + } + } + + if (s.Contains("")) + italicOn = false; + + if (s.Contains("")) + boldOn = false; + + if (s.Contains("")) + underlineOn = false; + + lineSb.Append(HtmlUtil.RemoveHtmlTags(pre + line)); + count++; + } + string text = lineSb.ToString(); + int noOfLines = Utilities.CountTagInText(text, '|') + 1; + if (Utilities.CountTagInText(text, "{y:i}") == noOfLines && text.StartsWith("{y:i}")) + text = "{Y:i}" + text.Replace("{y:i}", string.Empty); + else if (Utilities.CountTagInText(text, "{y:b}") == noOfLines && text.StartsWith("{y:b}")) + text = "{Y:b}" + text.Replace("{y:b}", string.Empty); + else if (Utilities.CountTagInText(text, "{y:u}") == noOfLines && text.StartsWith("{y:u}")) + text = "{Y:u}" + text.Replace("{y:u}", string.Empty); + else if (Utilities.CountTagInText(text, "{y:u}{y:i}") == noOfLines && text.StartsWith("{y:u}{y:i}")) + text = "{Y:u}{Y:i}" + text.Replace("{y:u}", string.Empty).Replace("{y:i}", string.Empty); + else if (Utilities.CountTagInText(text, "{y:i}{y:u}") == noOfLines && text.StartsWith("{y:i}{y:u}")) + text = "{Y:i}{Y:u}" + text.Replace("{y:i}", string.Empty).Replace("{y:u}", string.Empty); + else if (Utilities.CountTagInText(text, "{y:i}{y:b}") == noOfLines && text.StartsWith("{y:i}{y:b}")) + text = "{Y:i}{Y:b}" + text.Replace("{y:i}", string.Empty).Replace("{y:b}", string.Empty); + else if (Utilities.CountTagInText(text, "{y:b}{y:i}") == noOfLines && text.StartsWith("{y:b}{y:i}")) + text = "{Y:b}{Y:i}" + text.Replace("{y:i}", string.Empty).Replace("{y:b}", string.Empty); + else if (Utilities.CountTagInText(text, "{y:b}{y:u}") == noOfLines && text.StartsWith("{y:b}{y:u}")) + text = "{Y:b}{Y:u}" + text.Replace("{y:b}", string.Empty).Replace("{y:u}", string.Empty); + else if (Utilities.CountTagInText(text, "{y:u}{y:b}") == noOfLines && text.StartsWith("{y:u}{y:b}")) + text = "{Y:u}{Y:b}" + text.Replace("{y:u}", string.Empty).Replace("{y:b}", string.Empty); + + if (Utilities.CountTagInText(text, "{y:i}") == 1 && noOfLines == 1) + text = text.Replace("{y:i}", "{Y:i}"); + if (Utilities.CountTagInText(text, "{y:b}") == 1 && noOfLines == 1) + text = text.Replace("{y:b}", "{Y:b}"); + if (Utilities.CountTagInText(text, "{y:u}") == 1 && noOfLines == 1) + text = text.Replace("{y:u}", "{Y:u}"); + + sb.AppendLine(HtmlUtil.RemoveHtmlTags(text)); + } + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + _errors = new StringBuilder(); + Errors = null; + _lineNumber = 0; + + foreach (string line in lines) + { + _lineNumber++; + string s = RemoveIllegalSpacesAndFixEmptyCodes(line); + if (RegexMicroDvdLine.IsMatch(s)) + { + try + { + int textIndex = GetTextStartIndex(s); + if (textIndex < s.Length) + { + string text = s.Substring(textIndex); + string temp = s.Substring(0, textIndex - 1); + string[] frames = temp.Replace("][", ":").Replace("[", string.Empty).Replace("]", string.Empty).Split(':'); + + int startFrame = int.Parse(frames[0]); + int endFrame = int.Parse(frames[1]); + + string post = string.Empty; + string[] parts = text.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); + int count = 0; + var lineSb = new StringBuilder(); + + foreach (string s2 in parts) + { + if (count > 0) + lineSb.AppendLine(); + + s = s2.Trim(); + var pre = new StringBuilder(); + string singlePost = string.Empty; + for (int i = 0; i < 5; i++) + { + if (s.StartsWith("{Y:i}")) + { + s = s.Remove(0, 5); + pre.Append(""); + post = "" + post; + } + else if (s.StartsWith("{Y:b}")) + { + s = s.Remove(0, 5); + pre.Append(""); + post = "" + post; + } + else if (s.StartsWith("{Y:u}")) + { + s = s.Remove(0, 5); + pre.Append(""); + post = "" + post; + } + else if (s.StartsWith("{y:i}")) + { + s = s.Remove(0, 5); + singlePost = "" + singlePost; + pre.Append(""); + } + else if (s.StartsWith("{y:b}")) + { + s = s.Remove(0, 5); + singlePost = "" + singlePost; + pre.Append(""); + } + else if (s.StartsWith("{y:u}")) + { + s = s.Remove(0, 5); + singlePost = "" + singlePost; + pre.Append(""); + } + else if (s.StartsWith("{y:b,u}")) + { + s = s.Remove(0, 7); + singlePost = "" + singlePost; + pre.Append(""); + } + else if (s.StartsWith("{y:u,b}")) + { + s = s.Remove(0, 7); + singlePost = "" + singlePost; + pre.Append(""); + } + else if (s.StartsWith("{y:b,i}")) + { + s = s.Remove(0, 7); + singlePost = "" + singlePost; + pre.Append(""); + } + else if (s.StartsWith("{y:i,b}")) + { + s = s.Remove(0, 7); + singlePost = "" + singlePost; + pre.Append(""); + } + else if (s.StartsWith("{y:i,u}")) + { + s = s.Remove(0, 7); + singlePost = "" + singlePost; + pre.Append(""); + } + else if (s.StartsWith("{y:u,i}")) + { + s = s.Remove(0, 7); + singlePost = "" + singlePost; + pre.Append("<>"); + } + else if (s.StartsWith("{Y:b,u}") || s.StartsWith("{Y:u,b}")) + { + s = s.Remove(0, 7); + pre.Append(""); + post = "" + post; + } + else if (s.StartsWith("{Y:b,i}") || s.StartsWith("{Y:i,b}")) + { + s = s.Remove(0, 7); + pre.Append(""); + post = "" + post; + } + else if (s.StartsWith("{Y:i,u}") || s.StartsWith("{Y:u,i}")) + { + s = s.Remove(0, 7); + pre.Append(""); + post = "" + post; + } + else if (s.Contains("{c:$")) + { + int start = s.IndexOf("{c:$", StringComparison.Ordinal); + int end = s.IndexOf('}', start); + if (end > start) + { + string tag = s.Substring(start, end - start); + tag = tag.Remove(0, 4); + if (tag.Length == 6) + { + pre.Append(""); + s = s.Remove(start, end - start + 1); + singlePost = "" + singlePost; + } + } + } + else if (s.Contains("{C:$")) // uppercase=all lines + { + int start = s.IndexOf("{C:$", StringComparison.Ordinal); + int end = s.IndexOf('}', start); + if (end > start) + { + string tag = s.Substring(start, end - start); + tag = tag.Remove(0, 4); + if (tag.Length == 6) + { + pre.Append(""); + s = s.Remove(start, end - start + 1); + post = "" + post; + } + } + } + else if (s.Contains("{f:")) + { + int start = s.IndexOf("{f:", StringComparison.Ordinal); + int end = s.IndexOf('}', start); + if (end > start) + { + string tag = s.Substring(start, end - start); + tag = tag.Remove(0, 3).Trim(); + if (tag.Length > 0) + { + pre.Append(""); + s = s.Remove(start, end - start + 1); + singlePost = "" + singlePost; + } + } + } + else if (s.Contains("{F:")) // uppercase=all lines + { + int start = s.IndexOf("{F:", StringComparison.Ordinal); + int end = s.IndexOf('}', start); + if (end > start) + { + string tag = s.Substring(start, end - start); + tag = tag.Remove(0, 3).Trim(); + if (tag.Length > 0) + { + pre.Append(""); + s = s.Remove(start, end - start + 1); + post = "" + post; + } + } + } + else if (s.Contains("{s:")) + { + int start = s.IndexOf("{s:", StringComparison.Ordinal); + int end = s.IndexOf('}', start); + if (end > start) + { + string tag = s.Substring(start, end - start); + tag = tag.Remove(0, 3).Trim(); + if (tag.Length > 0) + { + pre.Append(""); + s = s.Remove(start, end - start + 1); + singlePost = "" + singlePost; + } + } + } + else if (s.Contains("{S:")) // uppercase=all lines + { + int start = s.IndexOf("{S:", StringComparison.Ordinal); + int end = s.IndexOf('}', start); + if (end > start) + { + string tag = s.Substring(start, end - start); + tag = tag.Remove(0, 3).Trim(); + if (tag.Length > 0) + { + pre.Append(""); + s = s.Remove(start, end - start + 1); + post = "" + post; + } + } + } + } + + s = s.Replace("{Y:i}", string.Empty).Replace("{y:i}", string.Empty); + s = s.Replace("{Y:b}", string.Empty).Replace("{y:b}", string.Empty); + s = s.Replace("{Y:u}", string.Empty).Replace("{y:u}", string.Empty); + lineSb.Append(pre + s + singlePost); + count++; + } + text = lineSb + post; + subtitle.Paragraphs.Add(new Paragraph(startFrame, endFrame, text)); + } + } + catch + { + _errorCount++; + if (_errors.Length < 2000) + _errors.AppendLine(string.Format(Configuration.Settings.Language.Main.LineNumberXErrorReadingFromSourceLineY, _lineNumber, line)); + } + } + else + { + _errorCount++; + if (_errors.Length < 2000) + _errors.AppendLine(string.Format(Configuration.Settings.Language.Main.LineNumberXErrorReadingFromSourceLineY, _lineNumber, line)); + } + } + + int j = 0; + foreach (Paragraph p in subtitle.Paragraphs) + { + Paragraph previous = subtitle.GetParagraphOrDefault(j - 1); + if (p.StartFrame == 0 && previous != null) + { + p.StartFrame = previous.EndFrame + 1; + } + if (p.EndFrame == 0) + { + p.EndFrame = p.StartFrame; + } + j++; + } + + subtitle.Renumber(); + Errors = _errors.ToString(); + } + + private static int GetTextStartIndex(string line) + { + int i = 0; + int tagCount = 0; + while (i < line.Length && tagCount < 4) + { + if (line[i] == '[' || line[i] == ']') + tagCount++; + i++; + } + return i; + } + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle71.cs b/libse/SubtitleFormats/UnknownSubtitle71.cs new file mode 100644 index 000000000..dd14d45e3 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle71.cs @@ -0,0 +1,217 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle71 : SubtitleFormat + { + + private static readonly Regex RegexTimeCode = new Regex(@"^ \d \d : \d \d : \d \d : \d \d $", RegexOptions.Compiled); + private static readonly Regex RegexTimeCode2 = new Regex(@"^\d \d : \d \d : \d \d : \d \d$", RegexOptions.Compiled); + + private enum ExpectingLine + { + Number, + TimeStart, + TimeEnd, + Text + } + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 71"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + private static string AddSpaces(string text) + { + if (string.IsNullOrEmpty(text)) + return " "; + + var sb = new StringBuilder(@" "); + for (int i = 0; i < text.Length; i++) + { + sb.Append(text[i]); + sb.Append(' '); + } + return sb.ToString(); + } + + private static string RemoveSpaces(string text) + { + if (string.IsNullOrEmpty(text)) + return string.Empty; + + text = text.Trim(); + for (int i = 0; i < text.Length; i++) + { + if (i % 2 == 1 && text[i] != ' ') + return text; + } + var sb = new StringBuilder(); + for (int i = 0; i < text.Length; i++) + { + if (i % 2 == 0) + sb.Append(text[i]); + } + return sb.ToString(); + } + + public override string ToText(Subtitle subtitle, string title) + { + // 1 . + // 0 0 : 0 0 : 0 4 : 1 2 + // 0 0 : 0 0 : 0 6 : 0 5 + // H a l l o ! + + // 2. + // 0 0 : 0 0 : 0 6 : 1 6 + // 0 0 : 0 0 : 0 7 : 2 0 + // G e t i n s i d e , m o m ! + // - I w a n t t o c o m e . + + const string paragraphWriteFormat = "{4} . {3}{3}{0}{3}{3}{1}{3}{3}{2}{3}{3}"; + var sb = new StringBuilder(); + int count = 0; + foreach (Paragraph p in subtitle.Paragraphs) + { + count++; + var text = AddSpaces(HtmlUtil.RemoveOpenCloseTags(p.Text, HtmlUtil.TagFont)); + sb.AppendLine(string.Format(paragraphWriteFormat, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), text, Environment.NewLine, count)); + } + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + Paragraph paragraph = null; + ExpectingLine expecting = ExpectingLine.Number; + _errorCount = 0; + + subtitle.Paragraphs.Clear(); + foreach (string line in lines) + { + if (expecting == ExpectingLine.Number && (RegexTimeCode.IsMatch(line) || RegexTimeCode2.IsMatch(line.Trim()))) + { + _errorCount++; + if (paragraph != null) + subtitle.Paragraphs.Add(paragraph); + paragraph = new Paragraph(); + expecting = ExpectingLine.TimeStart; + } + + if (line.TrimEnd().EndsWith('.') && Utilities.IsInteger(RemoveSpaces(line.Trim().TrimEnd('.').Trim()))) + { + if (paragraph != null) + subtitle.Paragraphs.Add(paragraph); + paragraph = new Paragraph(); + expecting = ExpectingLine.TimeStart; + } + else if (paragraph != null && expecting == ExpectingLine.TimeStart && (RegexTimeCode.IsMatch(line) || RegexTimeCode2.IsMatch(line.Trim()))) + { + string[] parts = RemoveSpaces(line.Trim()).Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 4) + { + try + { + var tc = DecodeTimeCode(parts); + paragraph.StartTime = tc; + expecting = ExpectingLine.TimeEnd; + } + catch + { + _errorCount++; + expecting = ExpectingLine.Number; + } + } + } + else if (paragraph != null && expecting == ExpectingLine.TimeEnd && (RegexTimeCode.IsMatch(line) || RegexTimeCode2.IsMatch(line.Trim()))) + { + string[] parts = RemoveSpaces(line.Trim()).Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 4) + { + try + { + var tc = DecodeTimeCode(parts); + paragraph.EndTime = tc; + expecting = ExpectingLine.Text; + } + catch + { + _errorCount++; + expecting = ExpectingLine.Number; + } + } + } + else + { + if (line == " " || line.Trim() == @"...........\...........") + { + } + else if (line == "*END*") + { + _errorCount++; + if (paragraph != null) + subtitle.Paragraphs.Add(paragraph); + paragraph = new Paragraph(); + expecting = ExpectingLine.Number; + } + else if (paragraph != null && expecting == ExpectingLine.Text) + { + if (line.Length > 0) + { + string s = RemoveSpaces(line); + paragraph.Text = (paragraph.Text + Environment.NewLine + s).Trim(); + if (paragraph.Text.Length > 2000) + { + _errorCount += 100; + return; + } + } + } + else if (line.Length > 1) + { + _errorCount++; + } + } + } + if (paragraph != null && !string.IsNullOrEmpty(paragraph.Text)) + subtitle.Paragraphs.Add(paragraph); + subtitle.Renumber(); + } + + private static string EncodeTimeCode(TimeCode time) + { + var s = time.ToHHMMSSFF(); + return AddSpaces(s); + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + var hour = int.Parse(parts[0]); + var minutes = int.Parse(parts[1]); + var seconds = int.Parse(parts[2]); + var frames = int.Parse(parts[3]); + return new TimeCode(hour, minutes, seconds, FramesToMillisecondsMax999(frames)); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/UnknownSubtitle72.cs b/libse/SubtitleFormats/UnknownSubtitle72.cs new file mode 100644 index 000000000..fa4aeed3b --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle72.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle72 : SubtitleFormat + { + //00:00:02.000 + //Junior Semifinal, part 1 + //Aidiba Talamunuer, Berezan + //Bogdan Voloshin, Yaroslavl + //Alexandr Doronin, Almaty + + //00:04:41.480 + //G. Zhubanova + //«Kui» + //Aidiba Talamunuer, Berezan + + //00:05:55.000 + //N. Mendigaliev + //«Steppe» + //Bogdan Voloshin, Yaroslavl + + private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d:\d\d:\d\d.\d{1,3}$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 72"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string paragraphWriteFormat = "{0}\r\n{1}\r\n"; + + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.AppendLine(string.Format(paragraphWriteFormat, p.StartTime.ToString().Replace(",", "."), p.Text)); + } + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + Paragraph paragraph = null; + _errorCount = 0; + subtitle.Paragraphs.Clear(); + for (int i = 0; i < lines.Count; i++) + { + string line = lines[i].TrimEnd(); + + if (line.Contains(':') && RegexTimeCodes.IsMatch(line)) + { + if (paragraph != null && string.IsNullOrEmpty(paragraph.Text)) + _errorCount++; + + paragraph = new Paragraph(); + if (TryReadTimeCodesLine(line, paragraph)) + { + subtitle.Paragraphs.Add(paragraph); + } + else + { + _errorCount++; + } + } + else if (paragraph != null && paragraph.Text.Length < 1000) + { + paragraph.Text = (paragraph.Text + Environment.NewLine + line).Trim(); + } + else + { + _errorCount++; + } + } + + int index = 0; + foreach (Paragraph p in subtitle.Paragraphs) + { + index++; + p.Text = p.Text.Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine); + Paragraph nextParagraph = subtitle.GetParagraphOrDefault(index); + p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + Utilities.GetOptimalDisplayMilliseconds(p.Text) + 100; + if (nextParagraph != null && p.EndTime.TotalMilliseconds >= nextParagraph.StartTime.TotalMilliseconds) + { + p.EndTime.TotalMilliseconds = nextParagraph.StartTime.TotalMilliseconds - 1; + } + } + subtitle.Renumber(); + } + + private bool TryReadTimeCodesLine(string line, Paragraph paragraph) + { + string[] parts = line.Split(':', '.'); + try + { + int startHours = int.Parse(parts[0]); + int startMinutes = int.Parse(parts[1]); + int startSeconds = int.Parse(parts[2]); + int startMilliseconds = int.Parse(parts[3]); + + if (parts[3].Length == 2) + _errorCount++; + + paragraph.StartTime = new TimeCode(startHours, startMinutes, startSeconds, startMilliseconds); + return true; + } + catch + { + return false; + } + } + + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle73.cs b/libse/SubtitleFormats/UnknownSubtitle73.cs new file mode 100644 index 000000000..22d9fdc9c --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle73.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle73 : SubtitleFormat + { + //59:00:22:09:14 00:22:12:04 02:15 + private static readonly Regex RegexTimeCodes = new Regex(@"^\d+:\d\d:\d\d:\d\d:\d\d \d\d:\d\d:\d\d:\d\d \d\d:\d\d$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 73"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + //59:00:22:09:14 00:22:12:04 02:15 + // + //LÕaria fresca arriva + // + //dai monti di Petr—polis + + var sb = new StringBuilder(); + int index = 0; + foreach (Paragraph p in subtitle.Paragraphs) + { + index++; + string text = HtmlUtil.RemoveHtmlTags(p.Text); + text = text.Replace(Environment.NewLine, "\n\n"); + sb.AppendFormat("{0}:{1} {2} {3}\n{4}\n\n", index, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), EncodeDuration(p), text); + } + return sb.ToString(); + } + + private static string EncodeDuration(Paragraph p) + { + return string.Format("{0:00}:{1:00}", p.Duration.Seconds, MillisecondsToFramesMaxFrameRate(p.Duration.Milliseconds)); + } + + private static string EncodeTimeCode(TimeCode time) + { + //00:03:15:22 (last is frame) + return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", time.Hours, time.Minutes, time.Seconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + Paragraph p = null; + subtitle.Paragraphs.Clear(); + _errorCount = 0; + foreach (string line in lines) + { + string s = line; + if (RegexTimeCodes.IsMatch(s)) + { + s = s.Remove(0, s.IndexOf(':') + 1).Trim(); + var temp = s.Split(' '); + if (temp.Length > 1) + { + string start = temp[0]; + string end = temp[1]; + + string[] startParts = start.Split(':'); + string[] endParts = end.Split(':'); + if (startParts.Length == 4 && endParts.Length == 4) + { + try + { + p = new Paragraph(DecodeTimeCode(startParts), DecodeTimeCode(endParts), string.Empty); + subtitle.Paragraphs.Add(p); + } + catch (Exception exception) + { + _errorCount++; + System.Diagnostics.Debug.WriteLine(exception.Message); + } + } + } + } + else if (string.IsNullOrWhiteSpace(line)) + { + // skip empty lines + } + else if (p == null) + { + _errorCount++; + } + else + { + p.Text = (p.Text + Environment.NewLine + line).Trim(); + if (p.Text.Length > 1000) + { + _errorCount += 10; + return; + } + } + } + + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + //00:00:07:12 + var hour = int.Parse(parts[0]); + var minutes = int.Parse(parts[1]); + var seconds = int.Parse(parts[2]); + var frames = int.Parse(parts[3]); + return new TimeCode(hour, minutes, seconds, FramesToMillisecondsMax999(frames)); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/UnknownSubtitle74.cs b/libse/SubtitleFormats/UnknownSubtitle74.cs new file mode 100644 index 000000000..161345704 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle74.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle74 : SubtitleFormat + { + //07:02:27 + //>> GOOD MORNING AND WELCOME TO THE FALL 2014 COMMENCEMENT CEREMONY, A TIME TO RECOGNIZE OUR GRADUATING SENIORS. + //07:02:43 + //DURING YOUR TIME HERE, MICHIGAN STATE UNIVERSITY HAS POLLEDLY RECAST ITS LAND-GRANT MISSION TO MEET NEW CHALLENGES AND OPPORTUNITIES AND TO INNOVATE OUR FUTURE. + //07:02:54 + //OUR PLANS AND ACTIONS STEM FROM OUR CORE INTERWOVEN VALUES OF QUALITY, INCLUSION AND CONNECTIVITY. + //07:03:02 + + private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d:\d\d:\d\d$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 74"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string paragraphWriteFormat = "{0}\r\n{1}\r\n"; + + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.AppendFormat(paragraphWriteFormat, GetTimeCode(p), p.Text); + } + return sb.ToString().Trim(); + } + + private static string GetTimeCode(Paragraph p) + { + int seconds = p.StartTime.Seconds; + if (p.StartTime.Milliseconds >= 500) + seconds++; + return string.Format("{0:00}:{1:00}:{2:00}", p.StartTime.Hours, p.StartTime.Minutes, seconds); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + subtitle.Paragraphs.Clear(); + int i = 0; + Paragraph paragraph = null; + while (i < lines.Count) + { + string line = lines[i].TrimEnd(); + string next = string.Empty; + if (i + 1 < lines.Count) + next = lines[i + 1]; + + if (line.Length == 8 && line[2] == ':' && RegexTimeCodes.IsMatch(line) && !RegexTimeCodes.IsMatch(next)) + { + paragraph = new Paragraph(); + if (TryReadTimeCodesLine(line, paragraph)) + { + paragraph.Text = next; + if (!string.IsNullOrWhiteSpace(paragraph.Text)) + { + subtitle.Paragraphs.Add(paragraph); + i++; + } + else + { + _errorCount++; + } + } + else if (!string.IsNullOrWhiteSpace(line)) + { + _errorCount++; + } + } + else + { + if (paragraph != null && paragraph.Text.Length < 500) + { + paragraph.Text = paragraph.Text + Environment.NewLine + line; + } + else + { + _errorCount++; + return; + } + } + i++; + } + + foreach (Paragraph p in subtitle.Paragraphs) + p.Text = p.Text.Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine); + + int index = 0; + foreach (Paragraph p in subtitle.Paragraphs) + { + index++; + Paragraph nextParagraph = subtitle.GetParagraphOrDefault(index); + if (nextParagraph != null) + p.EndTime.TotalMilliseconds = nextParagraph.StartTime.TotalMilliseconds - 1; + else + p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + 2500; + p.Text = p.Text.Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine); + } + + subtitle.Renumber(); + } + + private static bool TryReadTimeCodesLine(string line, Paragraph paragraph) + { + string[] parts = line.Split(':'); + try + { + int startHours = int.Parse(parts[0]); + int startMinutes = int.Parse(parts[1]); + int startSeconds = int.Parse(parts[2]); + paragraph.StartTime = new TimeCode(startHours, startMinutes, startSeconds, 0); + return true; + } + catch + { + return false; + } + } + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle75.cs b/libse/SubtitleFormats/UnknownSubtitle75.cs new file mode 100644 index 000000000..1f6f42b78 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle75.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle75 : SubtitleFormat + { + +//BOSTA - English +//REEL 1 +//0001: 124+12 127+12 +//Get going, you idiot! +//0002: 139+13 143+07 +//They scorn it, but they don't +//know its true value. +//0003: 143+12 146+09 +//Move on, move on! +//0004: 147+04 151+00 +//In 1943, it was one of a kind. +//0005: 159+00 161+15 +//... +//1083: 1575+05 1583+09 +//THE END IS THE BEGINNING +//1084: 1918+12 0+00 +//END OF THE FILM + + private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d\d\d:\s+\d+\+\d+\s+\d+\+\d+$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 75"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string paragraphWriteFormat = "{0:0000}: {1} {2}\r\n{3}"; + + var sb = new StringBuilder(); + sb.AppendLine(title); + sb.AppendLine("REEL 1 "); + for (int index = 0; index < subtitle.Paragraphs.Count; index++) + { + Paragraph p = subtitle.Paragraphs[index]; + sb.AppendFormat(paragraphWriteFormat, index + 1, GetTimeCode(p.StartTime), GetTimeCode(p.EndTime), p.Text); + sb.AppendLine(); + } + return sb.ToString().Trim(); + } + + private static string GetTimeCode(TimeCode tc) + { + var seconds = (int)tc.TotalSeconds; + var frames = MillisecondsToFrames(tc.Milliseconds); + return string.Format("{0:00}+{1:00}", seconds, frames); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + subtitle.Paragraphs.Clear(); + int i = 0; + Paragraph paragraph = null; + while (i < lines.Count) + { + string line = lines[i].TrimEnd(); + if (line.Length > 8 && line[4] == ':' && RegexTimeCodes.IsMatch(line)) + { + if (paragraph != null) + { + if (!string.IsNullOrWhiteSpace(paragraph.Text)) + { + subtitle.Paragraphs.Add(paragraph); + } + else + { + _errorCount++; + } + } + + paragraph = new Paragraph(); + var arr = line.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); + if (arr.Length == 3) + { + try + { + paragraph.StartTime = TryReadTimeCodesLine(arr[1]); + paragraph.EndTime = TryReadTimeCodesLine(arr[2]); + } + catch (Exception) + { + _errorCount++; + } + } + else + { + _errorCount++; + } + } + else + { + if (paragraph != null && paragraph.Text.Length < 500) + { + paragraph.Text = (paragraph.Text + Environment.NewLine + line).Trim(); + } + else if (paragraph != null && paragraph.Text.Length > 500) + { + _errorCount++; + return; + } + } + i++; + } + if (paragraph != null && !string.IsNullOrWhiteSpace(paragraph.Text) && paragraph.Text != "END OF THE FILM") + { + subtitle.Paragraphs.Add(paragraph); + } + subtitle.Renumber(); + } + + private static TimeCode TryReadTimeCodesLine(string line) + { + string[] parts = line.Split('+'); + return new TimeCode(0, 0, int.Parse(parts[0]), FramesToMillisecondsMax999(int.Parse(parts[1]))); + } + + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle76.cs b/libse/SubtitleFormats/UnknownSubtitle76.cs new file mode 100644 index 000000000..bbc1b1668 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle76.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle76 : SubtitleFormat + { + public override string Extension + { + get { return ".xml"; } + } + + public override string Name + { + get { return "Unknown 76"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + string xmlAsString = sb.ToString().Trim(); + if (xmlAsString.Contains(" 0; + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + } + } + return false; + } + + internal static string ConvertToTimeString(TimeCode time) + { + return Convert.ToInt64(Math.Round(time.TotalMilliseconds)).ToString(CultureInfo.InvariantCulture); + } + + public override string ToText(Subtitle subtitle, string title) + { + var xml = new XmlDocument(); + xml.LoadXml(""); + + foreach (Paragraph p in subtitle.Paragraphs) + { + XmlNode paragraph = xml.CreateElement("text"); + paragraph.InnerText = p.Text; + + XmlAttribute dur = xml.CreateAttribute("d"); + dur.InnerText = ConvertToTimeString(p.Duration); + paragraph.Attributes.Append(dur); + + XmlAttribute start = xml.CreateAttribute("t"); + start.InnerText = ConvertToTimeString(p.StartTime); + paragraph.Attributes.Append(start); + + xml.DocumentElement.AppendChild(paragraph); + } + + return ToUtf8XmlString(xml).Replace(" xmlns=\"\"", string.Empty); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + var xml = new XmlDocument { XmlResolver = null }; + xml.LoadXml(sb.ToString().Trim()); + + foreach (XmlNode node in xml.DocumentElement.SelectNodes("text")) + { + try + { + string start = node.Attributes["t"].InnerText; + string dur = node.Attributes["d"].InnerText; + TimeCode startTimeCode = GetTimeCode(start); + var endTimeCode = new TimeCode(startTimeCode.TotalMilliseconds + GetTimeCode(dur).TotalMilliseconds); + var p = new Paragraph(startTimeCode, endTimeCode, node.InnerText.Replace(" ", " ").Replace(" ", " ")); + subtitle.Paragraphs.Add(p); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + _errorCount++; + } + } + subtitle.Renumber(); + } + + public static TimeCode GetTimeCode(string s) + { + return new TimeCode(Convert.ToDouble(s)); // ms + } + + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle77.cs b/libse/SubtitleFormats/UnknownSubtitle77.cs new file mode 100644 index 000000000..bfe8b39c0 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle77.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle77 : SubtitleFormat + { + private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d:\d\d:\d\d:\d\d\s+->\s+\d\d:\d\d:\d\d:\d\d$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 77"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + const string writeFormat = "{0} -> {1}{2}{3}{2}"; + foreach (Paragraph p in subtitle.Paragraphs) + { + //00:50:34:22 -> 00:50:39:13 + //Ich muss dafür sorgen, + //dass die Epsteins weiterleben + sb.AppendLine(string.Format(writeFormat, p.StartTime.ToHHMMSSFF(), p.EndTime.ToHHMMSSFF(), Environment.NewLine, HtmlUtil.RemoveHtmlTags(p.Text, true))); + } + return sb.ToString(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + //00:03:15:22 -> 00:03:23:10 This is line one. + //This is line two. + Paragraph p = null; + subtitle.Paragraphs.Clear(); + _errorCount = 0; + foreach (string line in lines) + { + if (RegexTimeCodes.IsMatch(line)) + { + string temp = line.Substring(0, RegexTimeCodes.Match(line).Length); + int indexOfSeparator = temp.IndexOf("->", StringComparison.Ordinal); + string start = temp.Substring(0, indexOfSeparator).Trim(); + string end = temp.Substring(indexOfSeparator + 2).Trim(); + + string[] startParts = start.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + string[] endParts = end.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + if (startParts.Length == 4 && endParts.Length == 4) + { + p = new Paragraph(DecodeTimeCode(startParts), DecodeTimeCode(endParts), string.Empty); + subtitle.Paragraphs.Add(p); + } + } + else if (string.IsNullOrWhiteSpace(line)) + { + // skip these lines + } + else if (p != null) + { + if (string.IsNullOrEmpty(p.Text)) + p.Text = line; + else + p.Text = p.Text + Environment.NewLine + line; + } + } + + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + //00:00:07:12 + var hour = int.Parse(parts[0]); + var minutes = int.Parse(parts[1]); + var seconds = int.Parse(parts[2]); + var frames = int.Parse(parts[3]); + return new TimeCode(hour, minutes, seconds, FramesToMillisecondsMax999(frames)); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/UnknownSubtitle78.cs b/libse/SubtitleFormats/UnknownSubtitle78.cs new file mode 100644 index 000000000..87d0e3d1b --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle78.cs @@ -0,0 +1,230 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + class UnknownSubtitle78 : SubtitleFormat + { + public override string Extension + { + get { return ".xml"; } + } + + public override string Name + { + get { return "Unknown 78"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string xmpTemplate = @" + + + CCTV Subtitle Sequence File + 1.0 + 1 + CG1 + Jetsen + CCTV Subtitle Sequence Generate by Jetsen + [YYYY-MM-DD] + [YYYY-MM-DD] + 1 + + 0 + 0 + + HD_1080_25i + 1 + + + + 1008 + 2 + 2 + [TIME_CODE_FIRST] + [TIME_CODE_LAST] + 0 + 66373 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IsContinuousClip=FALSE + + + 0 + 0 + 0 + + + 0 + 52 + 0 + + + 52 + 53 + 0 + + + +"; + + const string paragraphTemplate = @" + 00:00:15:09 + 00:00:16:14 + + + + + + + + 0 + 0 + 0 + + + 0 + 29 + 0 + + + 29 + 30 + 0 + "; + var xml = new XmlDocument(); + var firstTimeCode = new TimeCode(0); + var lastTimeCode = new TimeCode(0); + if (subtitle.Paragraphs.Count > 0) + { + firstTimeCode = subtitle.Paragraphs[0].StartTime; + lastTimeCode = subtitle.Paragraphs[subtitle.Paragraphs.Count - 1].StartTime; + } + string today = DateTime.Now.ToString("YYYY-mm-DD"); + xml.LoadXml(xmpTemplate.Replace('\'', '"').Replace("[YYYY-MM-DD]", today).Replace("[TIME_CODE_FIRST]", firstTimeCode.ToHHMMSSFF()).Replace("[TIME_CODE_LAST]", lastTimeCode.ToHHMMSSFF())); + + var paragraphInsertNode = xml.DocumentElement.SelectSingleNode("TextSection"); + foreach (Paragraph p in subtitle.Paragraphs) + { + XmlNode paragraph = xml.CreateElement("TextScreen"); + paragraph.InnerXml = paragraphTemplate; + paragraph.SelectSingleNode("TimeCodeIn").InnerText = p.StartTime.ToHHMMSSFF(); + paragraph.SelectSingleNode("TimeCodeOut").InnerText = p.EndTime.ToHHMMSSFF(); + var textBlockNodes = paragraph.SelectNodes("TextBlock"); + textBlockNodes[0].InnerText = p.Text; + paragraphInsertNode.AppendChild(paragraph); + } + return ToUtf8XmlString(xml).Replace(" xmlns=\"\"", string.Empty); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + var xmlAsText = sb.ToString().Trim(); + if (!xmlAsText.Contains("") || !xmlAsText.Contains("")) + { + return; + } + + try + { + var xml = new XmlDocument { XmlResolver = null }; + xml.LoadXml(xmlAsText); + + foreach (XmlNode node in xml.DocumentElement.SelectNodes("TextSection/TextScreen")) + { + try + { + var timeCodeIn = DecodeTimeCode(node.SelectSingleNode("TimeCodeIn").InnerText); + var timeCodeOut = DecodeTimeCode(node.SelectSingleNode("TimeCodeOut").InnerText); + sb.Clear(); + foreach (XmlNode textBlockNode in node.SelectNodes("TextBlock")) + { + sb.AppendLine(textBlockNode.InnerText); + } + var p = new Paragraph(timeCodeIn, timeCodeOut, sb.ToString().Trim()); + subtitle.Paragraphs.Add(p); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + _errorCount++; + } + } + subtitle.Renumber(); + + } + catch (Exception) + { + _errorCount++; + } + } + + private static TimeCode DecodeTimeCode(string timeCode) + { + //00:00:07:12 + var parts = timeCode.Split(':'); + var hour = int.Parse(parts[0]); + var minutes = int.Parse(parts[1]); + var seconds = int.Parse(parts[2]); + var frames = int.Parse(parts[3]); + return new TimeCode(hour, minutes, seconds, FramesToMillisecondsMax999(frames)); + } + + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle8.cs b/libse/SubtitleFormats/UnknownSubtitle8.cs new file mode 100644 index 000000000..0fc5945e9 --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle8.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle8 : SubtitleFormat + { + //00:04:04.219 + //The city council of long beach + + private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d:\d\d:\d\d.\d\d\d$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "Unknown 8"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string paragraphWriteFormat = "{0}\r\n{1}\r\n"; + + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.AppendFormat(paragraphWriteFormat, p.StartTime.ToString().Replace(',', '.'), p.Text); + } + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + var paragraph = new Paragraph(); + _errorCount = 0; + subtitle.Paragraphs.Clear(); + for (int i = 0; i < lines.Count; i++) + { + string line = lines[i].TrimEnd(); + string next = string.Empty; + if (i + 1 < lines.Count) + next = lines[i + 1]; + + if (line.Contains(':') && !next.Contains(':') && RegexTimeCodes.IsMatch(line) && !RegexTimeCodes.IsMatch(next)) + { + paragraph = new Paragraph(); + if (TryReadTimeCodesLine(line, paragraph)) + { + paragraph.Text = next; + if (!string.IsNullOrWhiteSpace(paragraph.Text)) + subtitle.Paragraphs.Add(paragraph); + } + else if (!string.IsNullOrWhiteSpace(line)) + { + _errorCount++; + } + } + else + { + _errorCount++; + } + } + + foreach (Paragraph p in subtitle.Paragraphs) + p.Text = p.Text.Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine); + + int index = 0; + foreach (Paragraph p in subtitle.Paragraphs) + { + index++; + Paragraph nextParagraph = subtitle.GetParagraphOrDefault(index); + if (nextParagraph != null) + p.EndTime.TotalMilliseconds = nextParagraph.StartTime.TotalMilliseconds - 1; + else + p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + 2500; + p.Text = p.Text.Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine); + } + + subtitle.Renumber(); + } + + private static bool TryReadTimeCodesLine(string line, Paragraph paragraph) + { + string[] parts = line.Split(':', '.'); + try + { + int startHours = int.Parse(parts[0]); + int startMinutes = int.Parse(parts[1]); + int startSeconds = int.Parse(parts[2]); + int startMilliseconds = int.Parse(parts[3]); + paragraph.StartTime = new TimeCode(startHours, startMinutes, startSeconds, startMilliseconds); + return true; + } + catch + { + return false; + } + } + } +} diff --git a/libse/SubtitleFormats/UnknownSubtitle9.cs b/libse/SubtitleFormats/UnknownSubtitle9.cs new file mode 100644 index 000000000..4d6f8a7ce --- /dev/null +++ b/libse/SubtitleFormats/UnknownSubtitle9.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UnknownSubtitle9 : SubtitleFormat + { + //00:04:04.219 + //The city council of long beach + + public override string Extension + { + get { return ".html"; } + } + + public override string Name + { + get { return "Unknown 9"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + sb.AppendLine("
"); + sb.AppendLine("
"); + sb.AppendLine("
"); + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.AppendLine(string.Format(" {2}",((long)(Math.Round(p.StartTime.TotalMilliseconds))).ToString(CultureInfo.InvariantCulture), ((long)(Math.Round(p.Duration.TotalMilliseconds))).ToString(CultureInfo.InvariantCulture), p.Text.Replace(Environment.NewLine, "
"))); + } + sb.AppendLine("
"); + sb.AppendLine("
"); + sb.AppendLine("
"); + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + //[♪techno music♪] + + var temp = new StringBuilder(); + foreach (string l in lines) + temp.Append(l); + string all = temp.ToString(); + if (!all.Contains("class=\"caption\"")) + return; + + _errorCount = 0; + subtitle.Paragraphs.Clear(); + for (int i = 0; i < lines.Count; i++) + { + string line = lines[i].Trim(); + + var indexOfStart = line.IndexOf("starttime=", StringComparison.Ordinal); + var indexOfDuration = line.IndexOf("duration=", StringComparison.Ordinal); + if (line.Contains("class=\"caption\"") && indexOfStart > 0 && indexOfDuration > 0) + { + string startTime = "0"; + int index = indexOfStart + 10; + while (index < line.Length && @"0123456789""'.,".Contains(line[index])) + { + if (@"0123456789,.".Contains(line[index])) + startTime += line[index]; + index++; + } + + string duration = "0"; + index = indexOfDuration + 9; + while (index < line.Length && @"0123456789""'.,".Contains(line[index])) + { + if (@"0123456789,.".Contains(line[index])) + duration += line[index]; + index++; + } + + string text = string.Empty; + index = line.IndexOf('>', indexOfDuration); + if (index > 0 && index + 1 < line.Length) + { + text = line.Substring(index + 1).Trim(); + index = text.IndexOf(" 0) + text = text.Substring(0, index); + text = text.Replace("
", Environment.NewLine); + } + + long startMilliseconds; + long durationMilliseconds; + if (text.Length > 0 && long.TryParse(startTime, out startMilliseconds) && long.TryParse(duration, out durationMilliseconds)) + subtitle.Paragraphs.Add(new Paragraph(text, startMilliseconds, startMilliseconds + durationMilliseconds)); + } + } + subtitle.Renumber(); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/Utx.cs b/libse/SubtitleFormats/Utx.cs new file mode 100644 index 000000000..2b68230b6 --- /dev/null +++ b/libse/SubtitleFormats/Utx.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class Utx : SubtitleFormat + { + + public override string Extension + { + get { return ".utx"; } + } + + public override string Name + { + get { return "UTX"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + //I'd forgotten. + //#0:02:58.21,0:03:00.16 + + //Were you somewhere far away? + //- Yes, four years in Switzerland. + //#0:03:02.15,0:03:06.14 + + const string paragraphWriteFormat = "{0}{1}#{2},{3}{1}"; + + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + string text = p.Text; + sb.AppendLine(string.Format(paragraphWriteFormat, p.Text, Environment.NewLine, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime))); + } + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + subtitle.Paragraphs.Clear(); + var text = new StringBuilder(); + for (int i = 0; i < lines.Count; i++) + { + string line = lines[i].Trim(); + + if (line.StartsWith('#')) + { + var timeParts = line.Split(new[] { '#', ',' }, StringSplitOptions.RemoveEmptyEntries); + if (timeParts.Length == 2) + { + try + { + TimeCode start = DecodeTimeCode(timeParts[0]); + TimeCode end = DecodeTimeCode(timeParts[1]); + subtitle.Paragraphs.Add(new Paragraph(start, end, text.ToString().Trim())); + } + catch + { + _errorCount++; + } + } + } + else if (line.Length > 0) + { + text.AppendLine(line.Trim()); + if (text.Length > 5000) + return; + } + else + { + text = new StringBuilder(); + } + } + subtitle.Renumber(); + } + + private static string EncodeTimeCode(TimeCode time) + { + //0:03:02.15 + return string.Format("{0}:{1:00}:{2:00}.{3:00}", time.Hours, time.Minutes, time.Seconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); + } + + private static TimeCode DecodeTimeCode(string timePart) + { + //0:03:02.15 + var parts = timePart.Split(new[] { ':', '.' }, StringSplitOptions.RemoveEmptyEntries); + + int hours = int.Parse(parts[0]); + int minutes = int.Parse(parts[1]); + int seconds = int.Parse(parts[2]); + int milliseconds = (int)((TimeCode.BaseUnit / Configuration.Settings.General.CurrentFrameRate) * int.Parse(parts[3])); + return new TimeCode(hours, minutes, seconds, milliseconds); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/UtxFrames.cs b/libse/SubtitleFormats/UtxFrames.cs new file mode 100644 index 000000000..17e1ef6a1 --- /dev/null +++ b/libse/SubtitleFormats/UtxFrames.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class UtxFrames : SubtitleFormat + { + + public override string Extension + { + get { return ".utx"; } + } + + public override string Name + { + get { return "UTX (frames)"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + //I'd forgotten. + //#2060,2188 + + //Were you somewhere far away? + //- Yes, four years in Switzerland. + //#3885,3926 + + const string paragraphWriteFormat = "{0}{1}#{2},{3}{1}"; + + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.AppendLine(string.Format(paragraphWriteFormat, p.Text, Environment.NewLine, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime))); + } + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + subtitle.Paragraphs.Clear(); + var text = new StringBuilder(); + for (int i = 0; i < lines.Count; i++) + { + string line = lines[i].Trim(); + + if (line.StartsWith('#')) + { + var timeParts = line.Split(new[] { '#', ',' }, StringSplitOptions.RemoveEmptyEntries); + if (timeParts.Length == 2) + { + try + { + TimeCode start = DecodeTimeCode(timeParts[0]); + TimeCode end = DecodeTimeCode(timeParts[1]); + subtitle.Paragraphs.Add(new Paragraph(start, end, text.ToString().Trim())); + } + catch + { + _errorCount++; + } + } + } + else if (line.Length > 0) + { + text.AppendLine(line.Trim()); + if (text.Length > 5000) + return; + } + else + { + text = new StringBuilder(); + } + } + subtitle.Renumber(); + } + + private static string EncodeTimeCode(TimeCode time) + { + long frames = (long)(time.TotalMilliseconds / (TimeCode.BaseUnit / Configuration.Settings.General.CurrentFrameRate)); + return frames.ToString(); + } + + private static TimeCode DecodeTimeCode(string timePart) + { + int milliseconds = (int)((TimeCode.BaseUnit / Configuration.Settings.General.CurrentFrameRate) * int.Parse(timePart)); + return new TimeCode(milliseconds); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/VocapiaSplit.cs b/libse/SubtitleFormats/VocapiaSplit.cs new file mode 100644 index 000000000..78ce814fc --- /dev/null +++ b/libse/SubtitleFormats/VocapiaSplit.cs @@ -0,0 +1,170 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class VocapiaSplit : SubtitleFormat + { + public override string Extension + { + get { return ".xml"; } + } + + public override string Name + { + get { return "Vocapia Split"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + this.LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > 0; + } + + public override string ToText(Subtitle subtitle, string title) + { + string xmlStructure = + "" + Environment.NewLine + + "" + Environment.NewLine + + "" + Environment.NewLine + + "" + Environment.NewLine + + ""; + + var xml = new XmlDocument(); + xml.LoadXml(xmlStructure); + xml.DocumentElement.Attributes["name"].InnerText = title; + + if (subtitle.Header != null && subtitle.Header.Contains("")); + + reel.AppendChild(paragraph); + } + + return ToUtf8XmlString(xml); + } + + private static string ToTimeCode(double totalMilliseconds) + { + return string.Format("{0:0##}", totalMilliseconds / TimeCode.BaseUnit); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + + string xmlString = sb.ToString(); + if (!xmlString.Contains("", Environment.NewLine); + text = text.Replace(" ", " "); + var p = new Paragraph(text, ParseTimeCode(start), ParseTimeCode(end)); + var spkIdAttr = node.Attributes["spkid"]; + if (spkIdAttr != null) + { + p.Extra = spkIdAttr.InnerText; + p.Actor = p.Extra; + } + subtitle.Paragraphs.Add(p); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + _errorCount++; + } + } + subtitle.Renumber(); + if (subtitle.Paragraphs.Count > 0) + subtitle.Header = xmlString; + } + + private static double ParseTimeCode(string s) + { + return Convert.ToDouble(s) * TimeCode.BaseUnit; + } + + public override bool HasStyleSupport + { + get { return true; } + } + + public static List GetStylesFromHeader(Subtitle subtitle) + { + var list = new List(); + foreach (Paragraph p in subtitle.Paragraphs) + { + if (!string.IsNullOrEmpty(p.Actor)) + { + if (list.IndexOf(p.Actor) < 0) + list.Add(p.Actor); + } + } + return list; + } + + } +} diff --git a/libse/SubtitleFormats/WebVTT.cs b/libse/SubtitleFormats/WebVTT.cs new file mode 100644 index 000000000..d1da15dbb --- /dev/null +++ b/libse/SubtitleFormats/WebVTT.cs @@ -0,0 +1,201 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + /// + /// http://www.whatwg.org/specs/web-apps/current-work/webvtt.html + /// + public class WebVTT : SubtitleFormat + { + + private static readonly Regex RegexTimeCodes = new Regex(@"^-?\d+:-?\d+:-?\d+\.-?\d+\s*-->\s*-?\d+:-?\d+:-?\d+\.-?\d+", RegexOptions.Compiled); + private static readonly Regex RegexTimeCodesMiddle = new Regex(@"^-?\d+:-?\d+\.-?\d+\s*-->\s*-?\d+:-?\d+:-?\d+\.-?\d+", RegexOptions.Compiled); + private static readonly Regex RegexTimeCodesShort = new Regex(@"^-?\d+:-?\d+\.-?\d+\s*-->\s*-?\d+:-?\d+\.-?\d+", RegexOptions.Compiled); + + public override string Extension + { + get { return ".vtt"; } + } + + public override string Name + { + get { return "WebVTT"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string timeCodeFormatHours = "{0:00}:{1:00}:{2:00}.{3:000}"; // hh:mm:ss.cc + const string paragraphWriteFormat = "{0} --> {1}{4}{2}{3}{4}"; + + var sb = new StringBuilder(); + sb.AppendLine("WEBVTT"); + sb.AppendLine(); + foreach (Paragraph p in subtitle.Paragraphs) + { + string start = string.Format(timeCodeFormatHours, p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, p.StartTime.Milliseconds); + string end = string.Format(timeCodeFormatHours, p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, p.EndTime.Milliseconds); + + string style = string.Empty; + if (!string.IsNullOrEmpty(p.Extra) && subtitle.Header == "WEBVTT") + style = p.Extra; + sb.AppendLine(string.Format(paragraphWriteFormat, start, end, FormatText(p), style, Environment.NewLine)); + } + return sb.ToString().Trim(); + } + + private static string FormatText(Paragraph p) + { + string text = p.Text; + while (text.Contains(Environment.NewLine + Environment.NewLine)) + text = text.Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine); + return text; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + Paragraph p = null; + bool textDone = true; + foreach (string line in lines) + { + string s = line; + bool isTimeCode = line.Contains("-->"); + if (isTimeCode && RegexTimeCodesMiddle.IsMatch(s)) + { + s = "00:" + s; // start is without hours, end is with hours + } + if (isTimeCode && RegexTimeCodesShort.IsMatch(s)) + { + s = "00:" + s.Replace("--> ", "--> 00:"); + } + + if (isTimeCode && RegexTimeCodes.IsMatch(s)) + { + textDone = false; + if (p != null) + { + subtitle.Paragraphs.Add(p); + p = null; + } + try + { + string[] parts = s.Replace("-->", "@").Split(new[] { '@' }, StringSplitOptions.RemoveEmptyEntries); + p = new Paragraph(); + p.StartTime = GetTimeCodeFromString(parts[0]); + p.EndTime = GetTimeCodeFromString(parts[1]); + } + catch (Exception exception) + { + System.Diagnostics.Debug.WriteLine(exception.Message); + _errorCount++; + p = null; + } + } + else if (subtitle.Paragraphs.Count == 0 && line.Trim() == "WEBVTT") + { + subtitle.Header = "WEBVTT"; + } + else if (p != null && !string.IsNullOrWhiteSpace(line)) + { + string text = line.Trim(); + if (!textDone) + p.Text = (p.Text + Environment.NewLine + text).Trim(); + } + else if (line.Length == 0) + { + textDone = true; + } + } + if (p != null) + subtitle.Paragraphs.Add(p); + subtitle.Renumber(); + } + + public override void RemoveNativeFormatting(Subtitle subtitle, SubtitleFormat newFormat) + { + foreach (Paragraph p in subtitle.Paragraphs) + { + if (p.Text.Contains('<')) + { + string text = p.Text; + text = RemoveTag("v", text); + text = RemoveTag("rt", text); + text = RemoveTag("ruby", text); + text = RemoveTag("c", text); + text = RemoveTag("span", text); + p.Text = text; + } + } + } + + public static List GetVoices(Subtitle subtitle) + { + var list = new List(); + if (subtitle != null && subtitle.Paragraphs != null) + { + foreach (Paragraph p in subtitle.Paragraphs) + { + string s = p.Text; + var startIndex = s.IndexOf("= 0) + { + int endIndex = s.IndexOf('>', startIndex); + if (endIndex > startIndex) + { + string voice = s.Substring(startIndex + 2, endIndex - startIndex - 2).Trim(); + if (!list.Contains(voice)) + list.Add(voice); + } + + if (startIndex == s.Length - 1) + startIndex = -1; + else + startIndex = s.IndexOf("= 0) + { + int indexOfEnd = text.IndexOf('>', indexOfTag); + if (indexOfEnd > 0) + { + text = text.Remove(indexOfTag, indexOfEnd - indexOfTag + 1); + text = text.Replace("", string.Empty); + } + } + return text; + } + + private static TimeCode GetTimeCodeFromString(string time) + { + // hh:mm:ss.mmm + string[] timeCode = time.Trim().Split(':', '.', ' '); + return new TimeCode(int.Parse(timeCode[0]), + int.Parse(timeCode[1]), + int.Parse(timeCode[2]), + int.Parse(timeCode[3])); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/WebVTTFileWithLineNumber.cs b/libse/SubtitleFormats/WebVTTFileWithLineNumber.cs new file mode 100644 index 000000000..6a7030bd0 --- /dev/null +++ b/libse/SubtitleFormats/WebVTTFileWithLineNumber.cs @@ -0,0 +1,217 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + /// + /// http://www.whatwg.org/specs/web-apps/current-work/webvtt.html + /// + public class WebVTTFileWithLineNumber : SubtitleFormat + { + + private static readonly Regex RegexTimeCodes = new Regex(@"^-?\d+:-?\d+:-?\d+\.-?\d+\s*-->\s*-?\d+:-?\d+:-?\d+\.-?\d+", RegexOptions.Compiled); + private static readonly Regex RegexTimeCodesMiddle = new Regex(@"^-?\d+:-?\d+\.-?\d+\s*-->\s*-?\d+:-?\d+:-?\d+\.-?\d+", RegexOptions.Compiled); + private static readonly Regex RegexTimeCodesShort = new Regex(@"^-?\d+:-?\d+\.-?\d+\s*-->\s*-?\d+:-?\d+\.-?\d+", RegexOptions.Compiled); + + public override string Extension + { + get { return ".vtt"; } + } + + public override string Name + { + get { return "WebVTT File with#"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string timeCodeFormatNoHours = "{0:00}:{1:00}.{2:000}"; // h:mm:ss.cc + const string timeCodeFormatHours = "{0}:{1:00}:{2:00}.{3:000}"; // h:mm:ss.cc + const string paragraphWriteFormat = "{0} --> {1}{4}{2}{3}{4}"; + + var sb = new StringBuilder(); + sb.AppendLine("WEBVTT FILE"); + sb.AppendLine(); + int count = 1; + foreach (Paragraph p in subtitle.Paragraphs) + { + string start = string.Format(timeCodeFormatNoHours, p.StartTime.Minutes, p.StartTime.Seconds, p.StartTime.Milliseconds); + string end = string.Format(timeCodeFormatNoHours, p.EndTime.Minutes, p.EndTime.Seconds, p.EndTime.Milliseconds); + + if (p.StartTime.Hours > 0 || p.EndTime.Hours > 0) + { + start = string.Format(timeCodeFormatHours, p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, p.StartTime.Milliseconds); + end = string.Format(timeCodeFormatHours, p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, p.EndTime.Milliseconds); + } + + string style = string.Empty; + if (!string.IsNullOrEmpty(p.Extra) && subtitle.Header == "WEBVTT FILE") + style = p.Extra; + sb.Append(count); + sb.AppendLine(); + sb.AppendLine(string.Format(paragraphWriteFormat, start, end, FormatText(p), style, Environment.NewLine)); + count++; + } + return sb.ToString().Trim(); + } + + private static string FormatText(Paragraph p) + { + string text = p.Text; + while (text.Contains(Environment.NewLine + Environment.NewLine)) + text = text.Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine); + return text; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + Paragraph p = null; + bool textDone = true; + foreach (string line in lines) + { + string s = line; + bool isTimeCode = line.Contains("-->"); + if (isTimeCode && RegexTimeCodesMiddle.IsMatch(s)) + { + s = "00:" + s; // start is without hours, end is with hours + } + if (isTimeCode && RegexTimeCodesShort.IsMatch(s)) + { + s = "00:" + s.Replace("--> ", "--> 00:"); + } + + if (isTimeCode && RegexTimeCodes.IsMatch(s)) + { + textDone = false; + if (p != null) + { + subtitle.Paragraphs.Add(p); + p = null; + } + try + { + string[] parts = s.Replace("-->", "@").Split(new[] { '@' }, StringSplitOptions.RemoveEmptyEntries); + p = new Paragraph(); + p.StartTime = GetTimeCodeFromString(parts[0]); + p.EndTime = GetTimeCodeFromString(parts[1]); + } + catch (Exception exception) + { + System.Diagnostics.Debug.WriteLine(exception.Message); + _errorCount++; + p = null; + } + } + else if (subtitle.Paragraphs.Count == 0 && line.Trim() == "WEBVTT FILE") + { + subtitle.Header = "WEBVTT FILE"; + } + else if (p != null && !string.IsNullOrWhiteSpace(line)) + { + string text = line.Trim(); + if (!textDone) + p.Text = (p.Text + Environment.NewLine + text).Trim(); + } + else if (line.Length == 0) + { + textDone = true; + } + } + if (subtitle.Header == null && subtitle.Header != "WEBVTT FILE") + { + subtitle.Paragraphs.Clear(); + _errorCount++; + } + if (p != null) + subtitle.Paragraphs.Add(p); + subtitle.Renumber(); + } + + public override void RemoveNativeFormatting(Subtitle subtitle, SubtitleFormat newFormat) + { + foreach (Paragraph p in subtitle.Paragraphs) + { + if (p.Text.Contains('<')) + { + string text = p.Text; + text = RemoveTag("v", text); + text = RemoveTag("rt", text); + text = RemoveTag("ruby", text); + text = RemoveTag("c", text); + text = RemoveTag("span", text); + p.Text = text; + } + } + } + + public static List GetVoices(Subtitle subtitle) + { + var list = new List(); + if (subtitle != null && subtitle.Paragraphs != null) + { + foreach (Paragraph p in subtitle.Paragraphs) + { + string s = p.Text; + var startIndex = s.IndexOf("= 0) + { + int endIndex = s.IndexOf('>', startIndex); + if (endIndex > startIndex) + { + string voice = s.Substring(startIndex + 2, endIndex - startIndex - 2).Trim(); + if (!list.Contains(voice)) + list.Add(voice); + } + + if (startIndex == s.Length - 1) + startIndex = -1; + else + startIndex = s.IndexOf("= 0) + { + int indexOfEnd = text.IndexOf('>', indexOfTag); + if (indexOfEnd > 0) + { + text = text.Remove(indexOfTag, indexOfEnd - indexOfTag + 1); + text = text.Replace("", string.Empty); + } + } + return text; + } + + private static TimeCode GetTimeCodeFromString(string time) + { + // hh:mm:ss.mmm + string[] timeCode = time.Trim().Split(':', '.', ' '); + return new TimeCode(int.Parse(timeCode[0]), + int.Parse(timeCode[1]), + int.Parse(timeCode[2]), + int.Parse(timeCode[3])); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/Wsb.cs b/libse/SubtitleFormats/Wsb.cs new file mode 100644 index 000000000..12bf00961 --- /dev/null +++ b/libse/SubtitleFormats/Wsb.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class Wsb : SubtitleFormat + { + public override string Extension + { + get { return ".WSB"; } + } + + public override string Name + { + get { return "WSB"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + + var sb = new StringBuilder(); + foreach (string line in lines) + sb.AppendLine(line); + + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + int index = 0; + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.AppendLine(string.Format("{0:0000} : {1},{2},10", index + 1, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime))); + sb.AppendLine("80 80 80"); + foreach (string line in p.Text.SplitToLines()) + sb.AppendLine("C1Y00 " + line.Trim()); + sb.AppendLine(); + index++; + } + return sb.ToString(); + } + + private static string EncodeTimeCode(TimeCode time) + { + //00:03:15:22 (last is frame) + return string.Format("{0:00}{1:00}{2:00}{3:00}", time.Hours, time.Minutes, time.Seconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + //01072508010729007 + _errorCount = 0; + Paragraph p = null; + subtitle.Paragraphs.Clear(); + subtitle.Header = null; + foreach (string line in lines) + { + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } + + var indexOf7001 = line.IndexOf("7\x01\x01\0", StringComparison.Ordinal); + var indexOfTen = line.IndexOf(" 10 ", StringComparison.Ordinal); + if (indexOf7001 >= 0 && indexOfTen > 0) + { + try + { + string text = line.Substring(0, indexOfTen).Trim(); + string time = line.Substring(indexOf7001 - 16, 16); + p = new Paragraph(DecodeTimeCode(time.Substring(0, 8)), DecodeTimeCode(time.Substring(8)), text); + subtitle.Paragraphs.Add(p); + } + catch (Exception exception) + { + System.Diagnostics.Debug.WriteLine(exception.Message); + _errorCount++; + } + } + else if (p != null) + { + _errorCount++; + } + } + if (p != null && !string.IsNullOrEmpty(p.Text)) + subtitle.Paragraphs.Add(p); + + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string time) + { + //00:00:07:12 + string hour = time.Substring(0, 2); + string minutes = time.Substring(2, 2); + string seconds = time.Substring(4, 2); + string frames = time.Substring(6, 2); + + return new TimeCode(int.Parse(hour), int.Parse(minutes), int.Parse(seconds), FramesToMillisecondsMax999(int.Parse(frames))); + } + + } +} \ No newline at end of file diff --git a/libse/SubtitleFormats/Xif.cs b/libse/SubtitleFormats/Xif.cs new file mode 100644 index 000000000..182f7c877 --- /dev/null +++ b/libse/SubtitleFormats/Xif.cs @@ -0,0 +1,237 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + class Xif : SubtitleFormat + { + public override string Extension + { + get { return ".xif"; } + } + + public override string Name + { + get { return "XIF"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string xmpTemplate = @" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"; + + const string paragraphTemplate = @" + + + + + + + + + + + + + + + + + + "; + + var xml = new XmlDocument(); + var lastTimeCode = new TimeCode(0); + if (subtitle.Paragraphs.Count > 0) + { + lastTimeCode = subtitle.Paragraphs[subtitle.Paragraphs.Count - 1].StartTime; + } + if (string.IsNullOrWhiteSpace(title)) + title = "Unknown"; + xml.LoadXml(xmpTemplate.Replace('\'', '"')); + var globalFileInfoNode = xml.DocumentElement.SelectSingleNode("FileHeader/GlobalFileInfo"); + globalFileInfoNode.Attributes["ProgrammeName"].InnerText = title; + globalFileInfoNode.Attributes["ProgrammeTitle"].InnerText = title; + globalFileInfoNode.Attributes["StopTime"].InnerText = lastTimeCode.ToHHMMSSFF(); + globalFileInfoNode.Attributes["NumberOfCaptions"].InnerText = subtitle.Paragraphs.Count.ToString(CultureInfo.InvariantCulture); + + + var fileBodyNode = xml.DocumentElement.SelectSingleNode("FileBody"); + foreach (Paragraph p in subtitle.Paragraphs) + { + XmlNode content = xml.CreateElement("ContentBlock"); + content.InnerXml = paragraphTemplate; + content.SelectSingleNode("ContentBlock/ThreadedObject/TimingObject/TimeIn").InnerText = p.StartTime.ToHHMMSSFF(); + content.SelectSingleNode("ContentBlock/ThreadedObject/TimingObject/TimeOut").InnerText = p.EndTime.ToHHMMSSFF(); + + var paragraphNode = content.SelectSingleNode("ContentBlock/ThreadedObject/Content/SubtitleText/Paragraph"); + var lines = HtmlUtil.RemoveHtmlTags(p.Text, true).SplitToLines(); + for (int i = 1; i < lines.Length + 1; i++) + { + var rowNode = xml.CreateElement("Row"); + + var attrNumber = xml.CreateAttribute("Number"); + attrNumber.InnerText = i.ToString(CultureInfo.InvariantCulture); + rowNode.Attributes.Append(attrNumber); + + var attrJust = xml.CreateAttribute("JustificationOverride"); + attrJust.InnerText = "Centre"; + rowNode.Attributes.Append(attrJust); + + var attrHighlight = xml.CreateAttribute("Highlight"); + attrHighlight.InnerText = "0"; + rowNode.Attributes.Append(attrHighlight); + + paragraphNode.AppendChild(rowNode); + } + for (int index = 0; index < lines.Length; index++) + { + var line = lines[index]; + + if (index > 0) + { + var hardReturnNode = xml.CreateElement("HardReturn"); + paragraphNode.AppendChild(hardReturnNode); + } + + var foregroundColorNode = xml.CreateElement("ForegroundColour"); + var attrColor = xml.CreateAttribute("Colour"); + attrColor.InnerText = "7"; + foregroundColorNode.Attributes.Append(attrColor); + paragraphNode.AppendChild(foregroundColorNode); + + var textNode = xml.CreateElement("Text"); + textNode.InnerText = line; + paragraphNode.AppendChild(textNode); + + } + fileBodyNode.AppendChild(content.SelectSingleNode("ContentBlock")); + } + return ToUtf8XmlString(xml, true).Replace(" xmlns=\"\"", string.Empty); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + var xmlAsText = sb.ToString().Trim(); + if (!xmlAsText.Contains(" GetYouTubeAnnotationStyles(Dictionary stylesWithCount); + } + + public IGetYouTubeAnnotationStyles GetYouTubeAnnotationStyles { get; set; } + + private bool _promtForStyles = true; + + public override string Extension + { + get { return ".xml"; } + } + + public const string NameOfFormat = "YouTube Annotations"; + + public override string Name + { + get { return NameOfFormat; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + _promtForStyles = false; + LoadSubtitle(subtitle, lines, fileName); + _promtForStyles = true; + return subtitle.Paragraphs.Count > 0; + } + + public override string ToText(Subtitle subtitle, string title) + { + string xmlStructure = + "" + Environment.NewLine + + "" + Environment.NewLine + + " " + Environment.NewLine + + " " + Environment.NewLine + + ""; + + var xml = new XmlDocument(); + xml.LoadXml(xmlStructure); + + XmlNode annotations = xml.DocumentElement.SelectSingleNode("annotations"); + + int count = 1; + foreach (Paragraph p in subtitle.Paragraphs) + { + // + // BUT now something inside is BROKEN! + // + // + // + // + // + // + // + + XmlNode annotation = xml.CreateElement("annotation"); + + XmlAttribute att = xml.CreateAttribute("id"); + att.InnerText = "annotation_" + count; + annotation.Attributes.Append(att); + + att = xml.CreateAttribute("author"); + att.InnerText = "Subtitle Edit"; + annotation.Attributes.Append(att); + + att = xml.CreateAttribute("type"); + att.InnerText = "text"; + annotation.Attributes.Append(att); + + att = xml.CreateAttribute("style"); + att.InnerText = "speech"; + annotation.Attributes.Append(att); + + XmlNode text = xml.CreateElement("TEXT"); + text.InnerText = p.Text; + annotation.AppendChild(text); + + XmlNode segment = xml.CreateElement("segment"); + annotation.AppendChild(segment); + + XmlNode movingRegion = xml.CreateElement("movingRegion"); + segment.AppendChild(movingRegion); + + att = xml.CreateAttribute("type"); + att.InnerText = "anchored"; + movingRegion.Attributes.Append(att); + + XmlNode anchoredRegion = xml.CreateElement("anchoredRegion"); + movingRegion.AppendChild(anchoredRegion); + att = xml.CreateAttribute("t"); + att.InnerText = EncodeTime(p.StartTime); + anchoredRegion.Attributes.Append(att); + att = xml.CreateAttribute("d"); + att.InnerText = "0"; + anchoredRegion.Attributes.Append(att); + + anchoredRegion = xml.CreateElement("anchoredRegion"); + movingRegion.AppendChild(anchoredRegion); + att = xml.CreateAttribute("t"); + att.InnerText = EncodeTime(p.EndTime); + anchoredRegion.Attributes.Append(att); + att = xml.CreateAttribute("d"); + att.InnerText = "0"; + anchoredRegion.Attributes.Append(att); + + annotations.AppendChild(annotation); + count++; + } + + return ToUtf8XmlString(xml); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + if (!sb.ToString().Contains("") || !sb.ToString().Contains("")) + return; + var xml = new XmlDocument { XmlResolver = null }; + try + { + string xmlText = sb.ToString(); + xml.LoadXml(xmlText); + var styles = new List { "speech" }; + + if (_promtForStyles) + { + var stylesWithCount = new Dictionary(); + foreach (XmlNode node in xml.SelectNodes("//annotation")) + { + try + { + if (node.Attributes["style"] != null && node.Attributes["style"].Value != null) + { + string style = node.Attributes["style"].Value; + + XmlNode textNode = node.SelectSingleNode("TEXT"); + XmlNodeList regions = node.SelectNodes("segment/movingRegion/anchoredRegion"); + + if (regions.Count != 2) + regions = node.SelectNodes("segment/movingRegion/rectRegion"); + + if (textNode != null && regions.Count == 2) + { + if (stylesWithCount.ContainsKey(style)) + stylesWithCount[style]++; + else + stylesWithCount.Add(style, 1); + } + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + } + } + if (stylesWithCount.Count > 1 && GetYouTubeAnnotationStyles != null) + { + styles = GetYouTubeAnnotationStyles.GetYouTubeAnnotationStyles(stylesWithCount); + } + else + { + styles.Clear(); + foreach (var k in stylesWithCount.Keys) + styles.Add(k); + } + } + else + { + styles.Add("popup"); + styles.Add("anchored"); + } + + foreach (XmlNode node in xml.SelectNodes("//annotation")) + { + try + { + if (node.Attributes["style"] != null && styles.Contains(node.Attributes["style"].Value)) + { + XmlNode textNode = node.SelectSingleNode("TEXT"); + XmlNodeList regions = node.SelectNodes("segment/movingRegion/anchoredRegion"); + + if (regions.Count != 2) + regions = node.SelectNodes("segment/movingRegion/rectRegion"); + + if (textNode != null && regions.Count == 2) + { + string startTime = regions[0].Attributes["t"].Value; + string endTime = regions[1].Attributes["t"].Value; + var p = new Paragraph(); + p.StartTime = DecodeTimeCode(startTime); + p.EndTime = DecodeTimeCode(endTime); + p.Text = textNode.InnerText; + subtitle.Paragraphs.Add(p); + } + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + _errorCount++; + } + } + subtitle.Sort(SubtitleSortCriteria.StartTime); // force order by start time + subtitle.Renumber(); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + _errorCount = 1; + return; + } + } + + private static TimeCode DecodeTimeCode(string time) + { + string[] arr = time.Split(new[] { '.', ':' }, StringSplitOptions.RemoveEmptyEntries); + if (arr.Length == 3) + return new TimeCode(0, int.Parse(arr[0]), int.Parse(arr[1]), int.Parse(arr[2])); + return new TimeCode(int.Parse(arr[0]), int.Parse(arr[1]), int.Parse(arr[2]), int.Parse(arr[3])); + } + + private static string EncodeTime(TimeCode timeCode) + { + //0:01:08.0 + return string.Format("{0}:{1:00}:{2:00}.{3}", timeCode.Hours, timeCode.Minutes, timeCode.Seconds, timeCode.Milliseconds); + } + + } +} diff --git a/libse/SubtitleFormats/YouTubeSbv.cs b/libse/SubtitleFormats/YouTubeSbv.cs new file mode 100644 index 000000000..83ca876fb --- /dev/null +++ b/libse/SubtitleFormats/YouTubeSbv.cs @@ -0,0 +1,184 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + /// + /// YouTube "SubViewer" format... I think YouTube tried to add "SubViewer 2.0" support but instread they created their own format... nice ;) + /// + public class YouTubeSbv : SubtitleFormat + { + private enum ExpectingLine + { + TimeCodes, + Text + } + + private Paragraph _paragraph; + private ExpectingLine _expecting = ExpectingLine.TimeCodes; + private static readonly Regex RegexTimeCodes = new Regex(@"^-?\d+:-?\d+:-?\d+[:,.]-?\d+,\d+:-?\d+:-?\d+[:,.]-?\d+$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".sbv"; } + } + + public override string Name + { + get { return "YouTube sbv"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string paragraphWriteFormat = "{0},{1}\r\n{2}\r\n\r\n"; + + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.AppendFormat(paragraphWriteFormat, FormatTime(p.StartTime), FormatTime(p.EndTime), p.Text); + } + return sb.ToString().Trim(); + } + + private static string FormatTime(TimeCode timeCode) + { + return string.Format("{0}:{1:00}:{2:00}.{3:000}", timeCode.Hours, timeCode.Minutes, timeCode.Seconds, timeCode.Milliseconds); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + //0:00:07.500,0:00:13.500 + //In den Bergen über Musanze in Ruanda feiert die Trustbank (Kreditnehmer-Gruppe) "Trususanze" ihren Erfolg. + + //0:00:14.000,0:00:17.000 + //Indem sie ihre Zukunft einander anvertraut haben, haben sie sich + + _paragraph = new Paragraph(); + _expecting = ExpectingLine.TimeCodes; + _errorCount = 0; + + subtitle.Paragraphs.Clear(); + for (int i = 0; i < lines.Count; i++) + { + string line = lines[i].TrimEnd(); + string next = string.Empty; + if (i + 1 < lines.Count) + next = lines[i + 1]; + + // A new line is missing between two paragraphs (buggy srt file) + if (_expecting == ExpectingLine.Text && i + 1 < lines.Count && + _paragraph != null && !string.IsNullOrEmpty(_paragraph.Text) && + RegexTimeCodes.IsMatch(lines[i])) + { + ReadLine(subtitle, string.Empty, string.Empty); + } + + ReadLine(subtitle, line, next); + } + if (!string.IsNullOrWhiteSpace(_paragraph.Text)) + subtitle.Paragraphs.Add(_paragraph); + + foreach (Paragraph p in subtitle.Paragraphs) + p.Text = p.Text.Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine); + + subtitle.Renumber(); + } + + private void ReadLine(Subtitle subtitle, string line, string next) + { + switch (_expecting) + { + case ExpectingLine.TimeCodes: + if (TryReadTimeCodesLine(line, _paragraph)) + { + _paragraph.Text = string.Empty; + _expecting = ExpectingLine.Text; + } + else if (!string.IsNullOrWhiteSpace(line)) + { + _errorCount++; + } + break; + case ExpectingLine.Text: + if (!string.IsNullOrWhiteSpace(line)) + { + if (_paragraph.Text.Length > 0) + _paragraph.Text += Environment.NewLine; + _paragraph.Text += RemoveBadChars(line).TrimEnd(); + } + else if (IsText(next)) + { + if (_paragraph.Text.Length > 0) + _paragraph.Text += Environment.NewLine; + _paragraph.Text += RemoveBadChars(line).TrimEnd(); + } + else + { + subtitle.Paragraphs.Add(_paragraph); + _paragraph = new Paragraph(); + _expecting = ExpectingLine.TimeCodes; + } + break; + } + } + + private static bool IsText(string text) + { + if (string.IsNullOrWhiteSpace(text) || Utilities.IsInteger(text) || RegexTimeCodes.IsMatch(text)) + return false; + + return true; + } + + private static string RemoveBadChars(string line) + { + return line.Replace('\0', ' '); + } + + private static bool TryReadTimeCodesLine(string line, Paragraph paragraph) + { + line = line.Replace('.', ':'); + line = line.Replace('،', ','); + line = line.Replace('¡', ':'); + + if (RegexTimeCodes.IsMatch(line)) + { + line = line.Replace(',', ':'); + string[] parts = line.Replace(" ", string.Empty).Split(':', ','); + try + { + int startHours = int.Parse(parts[0]); + int startMinutes = int.Parse(parts[1]); + int startSeconds = int.Parse(parts[2]); + int startMilliseconds = int.Parse(parts[3]); + int endHours = int.Parse(parts[4]); + int endMinutes = int.Parse(parts[5]); + int endSeconds = int.Parse(parts[6]); + int endMilliseconds = int.Parse(parts[7]); + paragraph.StartTime = new TimeCode(startHours, startMinutes, startSeconds, startMilliseconds); + paragraph.EndTime = new TimeCode(endHours, endMinutes, endSeconds, endMilliseconds); + return true; + } + catch + { + return false; + } + } + return false; + } + } +} diff --git a/libse/SubtitleFormats/YouTubeTranscript.cs b/libse/SubtitleFormats/YouTubeTranscript.cs new file mode 100644 index 000000000..9eee7ab5d --- /dev/null +++ b/libse/SubtitleFormats/YouTubeTranscript.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class YouTubeTranscript : SubtitleFormat + { + private static readonly Regex RegexTimeCodes = new Regex(@"^\d{1,3}:\d\d$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "YouTube Transcript"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + + var sb = new StringBuilder(); + foreach (string line in lines) + sb.AppendLine(line); + + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + int index = 0; + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.AppendLine(string.Format("{0}" + Environment.NewLine + "{1}", EncodeTimeCode(p.StartTime), HtmlUtil.RemoveHtmlTags(p.Text.Replace(Environment.NewLine, " ")))); + index++; + } + return sb.ToString(); + } + + private static string EncodeTimeCode(TimeCode time) + { + return string.Format("{0}:{1:00}", time.Hours * 60 + time.Minutes, time.Seconds); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + Paragraph p = null; + subtitle.Paragraphs.Clear(); + foreach (string line in lines) + { + if (RegexTimeCodes.IsMatch(line)) + { + p = new Paragraph(DecodeTimeCode(line), new TimeCode(0, 0, 0, 0), string.Empty); + subtitle.Paragraphs.Add(p); + } + else if (string.IsNullOrWhiteSpace(line)) + { + // skip these lines + } + else if (p != null) + { + if (string.IsNullOrEmpty(p.Text)) + p.Text = line; + else + p.Text = p.Text + Environment.NewLine + line; + + if (p.Text.Length > 800) + { + _errorCount++; + return; + } + } + } + foreach (Paragraph p2 in subtitle.Paragraphs) + { + p2.Text = Utilities.AutoBreakLine(p2.Text); + } + subtitle.RecalculateDisplayTimes(Configuration.Settings.General.SubtitleMaximumDisplayMilliseconds, null); + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string s) + { + string[] parts = s.Split(':'); + + var minutes = int.Parse(parts[0]); + var seconds = int.Parse(parts[1]); + + return new TimeCode(0, minutes, seconds, 0); + } + + } +} diff --git a/libse/SubtitleFormats/YouTubeTranscriptOneLine.cs b/libse/SubtitleFormats/YouTubeTranscriptOneLine.cs new file mode 100644 index 000000000..a32be37a5 --- /dev/null +++ b/libse/SubtitleFormats/YouTubeTranscriptOneLine.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class YouTubeTranscriptOneLine : SubtitleFormat + { + private static readonly Regex RegexTimeCodes = new Regex(@"^\d{1,3}:\d\d.+$", RegexOptions.Compiled); + + public override string Extension + { + get { return ".txt"; } + } + + public override string Name + { + get { return "YouTube Transcript one line"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + + var sb = new StringBuilder(); + foreach (string line in lines) + sb.AppendLine(line); + + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + const string writeFormat = "{0}{1}"; + foreach (Paragraph p in subtitle.Paragraphs) + { + sb.AppendLine(string.Format(writeFormat, EncodeTimeCode(p.StartTime), HtmlUtil.RemoveHtmlTags(p.Text.Replace(Environment.NewLine, " ")))); + } + return sb.ToString(); + } + + private static string EncodeTimeCode(TimeCode time) + { + return string.Format("{0}:{1:00}", time.Hours * 60 + time.Minutes, time.Seconds); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + subtitle.Paragraphs.Clear(); + char[] trimChars = { '–', '.', ';', ':' }; + foreach (string line in lines) + { + if (RegexTimeCodes.IsMatch(line)) + { + int splitter = line.IndexOf(':') + 3; + string text = line.Remove(0, splitter); + var p = new Paragraph(DecodeTimeCode(line.Substring(0, splitter)), new TimeCode(0, 0, 0, 0), text); + subtitle.Paragraphs.Add(p); + text = text.Trim().Trim(trimChars).Trim(); + if (text.Length > 0 && char.IsDigit(text[0])) + _errorCount++; + } + else + { + _errorCount += 2; + } + } + foreach (Paragraph p2 in subtitle.Paragraphs) + { + p2.Text = Utilities.AutoBreakLine(p2.Text); + } + subtitle.RecalculateDisplayTimes(Configuration.Settings.General.SubtitleMaximumDisplayMilliseconds, null); + subtitle.Renumber(); + } + + private static TimeCode DecodeTimeCode(string s) + { + string[] parts = s.Split(':'); + + string minutes = parts[0]; + string seconds = parts[1]; + + return new TimeCode(0, int.Parse(minutes), int.Parse(seconds), 0); + } + + } +} diff --git a/libse/SubtitleFormats/ZeroG.cs b/libse/SubtitleFormats/ZeroG.cs new file mode 100644 index 000000000..16d41c047 --- /dev/null +++ b/libse/SubtitleFormats/ZeroG.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class ZeroG : SubtitleFormat + { + + public override string Extension + { + get { return ".zeg"; } + } + + public override string Name + { + get { return "Zero G"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + //% Zero G 1.0 + + //E 1 0:50:20.22 0:50:21.38 Default NTP Die Frage ist: + //E 1 0:50:21.54 0:50:25.86 Default NTP Wieso habe ich überlebt? + //E 1 0:50:27.30 0:50:30.78 Default NTP Was habe ich richtig gemacht? \n Ich weiß es nicht. + const string paragraphWriteFormat = "E 1 {0} {1} Default NTP {2}"; + + var sb = new StringBuilder(); + sb.AppendLine("% Zero G 1.0"); + sb.AppendLine(); + foreach (Paragraph p in subtitle.Paragraphs) + { + string text = p.Text.Replace(Environment.NewLine, " \\n "); + sb.AppendLine(string.Format(paragraphWriteFormat, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), text)); + } + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + var regexTimeCode = new Regex(@"^E 1 \d:\d\d:\d\d.\d\d \d:\d\d:\d\d.\d\d Default NTP ", RegexOptions.Compiled); + //E 1 0:50:05.42 0:50:10.06 Default NTP + + subtitle.Paragraphs.Clear(); + foreach (string line in lines) + { + if (regexTimeCode.IsMatch(line)) + { + try + { + string timePart = line.Substring(4, 10).Trim(); + var start = DecodeTimeCode(timePart); + timePart = line.Substring(15, 10).Trim(); + var end = DecodeTimeCode(timePart); + var paragraph = new Paragraph(); + paragraph.StartTime = start; + paragraph.EndTime = end; + paragraph.Text = line.Substring(38).Replace(" \\n ", Environment.NewLine).Replace("\\n", Environment.NewLine); + subtitle.Paragraphs.Add(paragraph); + } + catch + { + _errorCount++; + } + } + } + subtitle.Renumber(); + } + + private static string EncodeTimeCode(TimeCode time) + { + //0:50:05.42 + return string.Format("{0:0}:{1:00}:{2:00}.{3:00}", time.Hours, time.Minutes, time.Seconds, time.Milliseconds / 10); + } + + private static TimeCode DecodeTimeCode(string timePart) + { + string[] parts = timePart.Split(new[] { ':', '.' }, StringSplitOptions.RemoveEmptyEntries); + int hours = int.Parse(parts[0]); + int minutes = int.Parse(parts[1]); + int seconds = int.Parse(parts[2]); + int milliseconds = int.Parse(parts[3]) * 10; + return new TimeCode(hours, minutes, seconds, milliseconds); + } + + } +} \ No newline at end of file diff --git a/libse/TarHeader.cs b/libse/TarHeader.cs new file mode 100644 index 000000000..c61f31700 --- /dev/null +++ b/libse/TarHeader.cs @@ -0,0 +1,40 @@ +using System; +using System.IO; +using System.Text; + +namespace Nikse.SubtitleEdit.Core +{ + public class TarHeader + { + public const int HeaderSize = 512; + + public string FileName { get; set; } + public long FileSizeInBytes { get; set; } + public long FilePosition { get; set; } + + private readonly Stream _stream; + + public TarHeader(Stream stream) + { + _stream = stream; + var buffer = new byte[HeaderSize]; + stream.Read(buffer, 0, HeaderSize); + FilePosition = stream.Position; + + FileName = Encoding.ASCII.GetString(buffer, 0, 100).Replace("\0", string.Empty); + + string sizeInBytes = Encoding.ASCII.GetString(buffer, 124, 11); + if (!string.IsNullOrEmpty(FileName) && Utilities.IsInteger(sizeInBytes)) + FileSizeInBytes = Convert.ToInt64(sizeInBytes.Trim(), 8); + } + + public void WriteData(string fileName) + { + var buffer = new byte[FileSizeInBytes]; + _stream.Position = FilePosition; + _stream.Read(buffer, 0, buffer.Length); + File.WriteAllBytes(fileName, buffer); + } + + } +} \ No newline at end of file diff --git a/libse/TarReader.cs b/libse/TarReader.cs new file mode 100644 index 000000000..476e94da5 --- /dev/null +++ b/libse/TarReader.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Nikse.SubtitleEdit.Core +{ + public class TarReader : IDisposable + { + public List Files { get; private set; } + private Stream _stream; + + public TarReader(string fileName) + { + var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + OpenTarFile(fs); + } + + public TarReader(Stream stream) + { + OpenTarFile(stream); + } + + private void OpenTarFile(Stream stream) + { + _stream = stream; + Files = new List(); + long length = stream.Length; + long pos = 0; + stream.Position = 0; + while (pos + 512 < length) + { + stream.Seek(pos, SeekOrigin.Begin); + var th = new TarHeader(stream); + if (th.FileSizeInBytes > 0) + Files.Add(th); + pos += TarHeader.HeaderSize + th.FileSizeInBytes; + if (pos % TarHeader.HeaderSize > 0) + pos += 512 - (pos % TarHeader.HeaderSize); + } + } + + public void Close() + { + _stream.Close(); + } + + public void Dispose() + { + if (_stream != null) + { + _stream.Dispose(); + _stream = null; + } + } + + } +} \ No newline at end of file diff --git a/libse/TaskbarList.cs b/libse/TaskbarList.cs new file mode 100644 index 000000000..79ab5b5c2 --- /dev/null +++ b/libse/TaskbarList.cs @@ -0,0 +1,234 @@ +using System; +using System.Runtime.InteropServices; + +namespace Nikse.SubtitleEdit.Core +{ + /// + /// Windows 7+ taskbar list - http://msdn.microsoft.com/en-us/library/windows/desktop/dd391692%28v=vs.85%29.aspx + /// + public static class TaskbarList + { + private static readonly Lazy SupportedLazy = new Lazy(() => Environment.OSVersion.Version >= new Version(6, 1)); + private static readonly Lazy TaskbarListLazy = new Lazy(() => (ITaskbarList3)new CLSID_TaskbarList()); + + public static bool Supported { get { return SupportedLazy.Value; } } + internal static ITaskbarList3 Taskbar { get { return TaskbarListLazy.Value; } } + + public static void MarkFullscreenWindow(IntPtr hwnd, bool fullScreen) + { + if (Supported && hwnd != IntPtr.Zero) + Taskbar.MarkFullscreenWindow(hwnd, fullScreen ? 1 : 0); + } + + public static void SetProgressState(IntPtr hwnd, TaskbarButtonProgressFlags state) + { + if (Supported && hwnd != IntPtr.Zero) + Taskbar.SetProgressState(hwnd, state); + } + + public static void SetProgressValue(IntPtr hwnd, double value, double max) + { + if (Supported && hwnd != IntPtr.Zero) + Taskbar.SetProgressValue(hwnd, (ulong)value, (ulong)max); + } + + [ClassInterface(ClassInterfaceType.None), + ComImport, Guid("56FDF344-FD6D-11d0-958A-006097C9A090")] + private class CLSID_TaskbarList + { + } + + } + + /// Extends ITaskbarList2 by exposing methods that support the unified launching and switching + /// taskbar button functionality added in Windows 7. This functionality includes thumbnail representations + /// and switch targets based on individual tabs in a tabbed application, thumbnail toolbars, notification + /// and status overlays, and progress indicators. + [ComImport, Guid("ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf"), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface ITaskbarList3 + { + // ITaskbarList + + /// Initializes the taskbar list object. This method must be + /// called before any other ITaskbarList methods can be called. + void HrInit(); + + /// Adds an item to the taskbar. + /// A handle to the window to be + /// added to the taskbar. + void AddTab(IntPtr hWnd); + + /// Deletes an item from the taskbar. + /// A handle to the window to be deleted + /// from the taskbar. + void DeleteTab(IntPtr hWnd); + + /// Activates an item on the taskbar. The window is not actually activated; + /// the window’s item on the taskbar is merely displayed as active. + /// A handle to the window on the taskbar to be displayed as active. + void ActivateTab(IntPtr hWnd); + + /// Marks a taskbar item as active but does not visually activate it. + /// A handle to the window to be marked as active. + void SetActiveAlt(IntPtr hWnd); + // ITaskbarList2 + + /// Marks a window as full-screen + /// + /// + void MarkFullscreenWindow(IntPtr hWnd, int fullscreen); + + /// Displays or updates a progress bar hosted in a taskbar button to show + /// the specific percentage completed of the full operation. + /// The handle of the window whose associated taskbar button is being used as + /// a progress indicator. + /// An application-defined value that indicates the proportion of the + /// operation that has been completed at the time the method is called. + /// An application-defined value that specifies the value ullCompleted will + /// have when the operation is complete. + void SetProgressValue(IntPtr hWnd, ulong ullCompleted, ulong ullTotal); + + /// Sets the type and state of the progress indicator displayed on a taskbar button. + /// The handle of the window in which the progress of an operation is being + /// shown. This window’s associated taskbar button will display the progress bar. + /// Flags that control the current state of the progress button. Specify + /// only one of the following flags; all states are mutually exclusive of all others. + void SetProgressState(IntPtr hWnd, TaskbarButtonProgressFlags tbpFlags); + + /// Informs the taskbar that a new tab or document thumbnail has been provided for + /// display in an application’s taskbar group flyout. + /// Handle of the tab or document window. This value is required and cannot + /// be NULL. + /// Handle of the application’s main window. This value tells the taskbar + /// which application’s preview group to attach the new thumbnail to. This value is required and + /// cannot be NULL. + void RegisterTab(IntPtr hWndTab, IntPtr hWndMDI); + + /// Removes a thumbnail from an application’s preview group when that tab or document is closed in the application. + /// The handle of the tab window whose thumbnail is being removed. This is the same + /// value with which the thumbnail was registered as part the group through ITaskbarList3::RegisterTab. + /// This value is required and cannot be NULL. + void UnregisterTab(IntPtr hWndTab); + + /// Inserts a new thumbnail into a tabbed-document interface (TDI) or multiple-document + /// interface (MDI) application’s group flyout or moves an existing thumbnail to a new position in + /// the application’s group. + /// The handle of the tab window whose thumbnail is being placed. This value + /// is required, must already be registered through ITaskbarList3::RegisterTab, and cannot be NULL. + /// The handle of the tab window whose thumbnail that hwndTab is + /// inserted to the left of. This handle must already be registered through ITaskbarList3::RegisterTab. + /// If this value is NULL, the new thumbnail is added to the end of the list. + void SetTabOrder(IntPtr hWndTab, IntPtr hWndInsertBefore); + + /// Informs the taskbar that a tab or document window has been made the active window. + /// Handle of the active tab window. This handle must already be registered + /// through ITaskbarList3::RegisterTab. This value can be NULL if no tab is active. + /// Handle of the application’s main window. This value tells the taskbar + /// which group the thumbnail is a member of. This value is required and cannot be NULL. + /// None, one, or both of the following values that specify a thumbnail + /// and peek view to use in place of a representation of the specific tab or document. + void SetTabActive(IntPtr hWndTab, IntPtr hWndMDI, UInt32 tbatFlags); + + /// Adds a thumbnail toolbar with a specified set of buttons to the thumbnail image of a window + /// in a taskbar button flyout. + /// The handle of the window whose thumbnail representation will receive the toolbar. + /// This handle must belong to the calling process. + /// The number of buttons defined in the array pointed to by pButton. The maximum + /// number of buttons allowed is 7. + /// A pointer to an array of THUMBBUTTON structures. Each THUMBBUTTON defines an + /// individual button to be added to the toolbar. Buttons cannot be added or deleted later, so this must + /// be the full defined set. Buttons also cannot be reordered, so their order in the array, which is the + /// order in which they are displayed left to right, will be their permanent order. + void ThumbBarAddButtons(IntPtr hWnd, uint cButtons, [MarshalAs(UnmanagedType.LPArray)] ThumbButton[] pButton); + + /// Shows, enables, disables, or hides buttons in a thumbnail toolbar as required by the + /// window’s current state. A thumbnail toolbar is a toolbar embedded in a thumbnail image of a window + /// in a taskbar button flyout. + /// The handle of the window whose thumbnail representation contains the toolbar. + /// The number of buttons defined in the array pointed to by pButton. + /// The maximum number of buttons allowed is 7. This array contains only structures that represent existing buttons that are being updated. + /// A pointer to an array of THUMBBUTTON structures. Each THUMBBUTTON defines an individual button. If the button already exists + /// (the iId value is already defined), then that existing button is updated with the information provided in the structure. + void ThumbBarUpdateButtons(IntPtr hWnd, uint cButtons, [MarshalAs(UnmanagedType.LPArray)] ThumbButton[] pButton); + + /// Specifies an image list that contains button images for a toolbar embedded in + /// a thumbnail image of a window in a taskbar button flyout. + /// The handle of the window whose thumbnail representation contains the + /// toolbar to be updated. This handle must belong to the calling process. + /// The handle of the image list that contains all button images to be used in the toolbar. + void ThumbBarSetImageList(IntPtr hWnd, IntPtr himl); + + /// Applies an overlay to a taskbar button to indicate application status or a notification to the user. + /// The handle of the window whose associated taskbar button receives the overlay. + /// This handle must belong to a calling process associated with the button’s application and must be + /// a valid HWND or the call is ignored. + /// The handle of an icon to use as the overlay. This should be a small icon, + /// measuring 16×16 pixels at 96 dots per inch (dpi). If an overlay icon is already applied to the + /// taskbar button, that existing overlay is replaced. + /// A pointer to a string that provides an alt text version of the + /// information conveyed by the overlay, for accessibility purposes. + void SetOverlayIcon(IntPtr hWnd, IntPtr hIcon, string pszDescription); + + /// Specifies or updates the text of the tooltip that is displayed when the mouse + /// pointer rests on an individual preview thumbnail in a taskbar button flyout. + /// The handle to the window whose thumbnail displays the tooltip. + /// This handle must belong to the calling process. + /// The pointer to the text to be displayed in the tooltip. This value can + /// be NULL, in which case the title of the window specified by hwnd is used as the tooltip. + void SetThumbnailTooltip(IntPtr hWnd, string pszTip); + + /// Selects a portion of a window’s client area to display as that window’s thumbnail in the taskbar. + /// The handle to a window represented in the taskbar. + /// A pointer to a RECT structure that specifies a selection within the window’s + /// client area, relative to the upper-left corner of that client area. To clear a clip that is already + /// in place and return to the default display of the thumbnail, set this parameter to NULL. + void SetThumbnailClip(IntPtr hWnd, IntPtr prcClip); + } + + internal enum ThumbButtonMask + { + Bitmap = 0x1, + Icon = 0x2, + Tooltip = 0x4, + Flags = 0x8 + } + + public struct ThumbButton + { +#pragma warning disable 0169 + + private ThumbButtonMask _mask; + private uint _id; + private uint _bitmap; + private IntPtr _icon; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] + public string Tip; + private ThumbButtonFlags _flags; + +#pragma warning restore 0169 + } + + [Flags] + internal enum ThumbButtonFlags + { + Enabled = 0, + Disabled = 0x1, + DismissionClick = 0x2, + NoBackground = 0x4, + Hidden = 0x8, + NonInteractive = 0x10 + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags")] + public enum TaskbarButtonProgressFlags + { + NoProgress = 0, + Indeterminate = 0x1, + Normal = 0x2, + Error = 0x4, + Paused = 0x8 + } + +} \ No newline at end of file diff --git a/libse/TextDraw.cs b/libse/TextDraw.cs new file mode 100644 index 000000000..b53d31e0e --- /dev/null +++ b/libse/TextDraw.cs @@ -0,0 +1,139 @@ +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Text; + +namespace Nikse.SubtitleEdit.Core +{ + public static class TextDraw + { + public static void DrawText(Font font, StringFormat sf, GraphicsPath path, StringBuilder sb, bool isItalic, bool isBold, bool isUnderline, float left, float top, ref bool newLine, float leftMargin, ref int pathPointsStart) + { + var next = new PointF(left, top); + + if (path.PointCount > 0) + { + int k = 0; + + var list = (PointF[])path.PathPoints.Clone(); // avoid using very slow path.PathPoints indexer!!! + for (int i = list.Length - 1; i >= 0; i--) + { + if (list[i].X > next.X) + next.X = list[i].X; + k++; + if (k > 60) + break; + if (i <= pathPointsStart && pathPointsStart != -1) + break; + } + } + + if (newLine) + { + next.X = leftMargin; + newLine = false; + pathPointsStart = path.PointCount; + } + + var fontStyle = FontStyle.Regular; + if (isItalic) + fontStyle |= FontStyle.Italic; + if (isBold) + fontStyle |= FontStyle.Bold; + if (isUnderline) + fontStyle |= FontStyle.Underline; + + try + { + path.AddString(sb.ToString(), font.FontFamily, (int)fontStyle, font.Size, next, sf); + } + catch + { + fontStyle = FontStyle.Regular; + try + { + path.AddString(sb.ToString(), font.FontFamily, (int)fontStyle, font.Size, next, sf); + } + catch + { + path.AddString(sb.ToString(), new FontFamily("Arial"), (int)fontStyle, font.Size, next, sf); + } + } + sb.Length = 0; + } + + public static float MeasureTextWidth(Font font, string text, bool bold) + { + using (var sf = new StringFormat { Alignment = StringAlignment.Near, LineAlignment = StringAlignment.Near }) + { + using (var path = new GraphicsPath()) + { + var sb = new StringBuilder(text); + bool newLine = false; + const int leftMargin = 0; + int pathPointsStart = -1; + DrawText(font, sf, path, sb, false, bold, false, 0, 0, ref newLine, leftMargin, ref pathPointsStart); + if (path.PathData.Points.Length == 0) + { + return 0; + } + + float width = 0; + var list = (PointF[])path.PathPoints.Clone(); // avoid using very slow path.PathPoints indexer!!! + int index = list.Length - 42; + if (index < 0) + index = 0; + for (int i = index; i < list.Length; i += 2) + { + if (list[i].X > width) + width = list[i].X; + } + int max = 52; + if (max > list.Length) + max = list.Length; + for (int i = 0; i < max; i += 2) + { + if (list[i].X > width) + width = list[i].X; + } + + return width; + } + } + } + + public static float MeasureTextHeight(Font font, string text, bool bold) + { + using (var sf = new StringFormat { Alignment = StringAlignment.Near, LineAlignment = StringAlignment.Near }) + { + using (var path = new GraphicsPath()) + { + var sb = new StringBuilder(text); + bool newLine = false; + const int leftMargin = 0; + int pathPointsStart = -1; + DrawText(font, sf, path, sb, false, bold, false, 0, 0, ref newLine, leftMargin, ref pathPointsStart); + + float height = 0; + var list = (PointF[])path.PathPoints.Clone(); // avoid using very slow path.PathPoints indexer!!! + int index = list.Length - 80; + if (index < 0) + index = 0; + for (int i = index; i < list.Length; i += 2) + { + if (list[i].Y > height) + height = list[i].Y; + } + + for (int i = 0; i < list.Length; i += 2) + { + if (list[i].Y > height) + height = list[i].Y; + } + + return height; + } + } + } + + } +} \ No newline at end of file diff --git a/libse/TimeCode.cs b/libse/TimeCode.cs new file mode 100644 index 000000000..c70f66e44 --- /dev/null +++ b/libse/TimeCode.cs @@ -0,0 +1,224 @@ +using System; +using Nikse.SubtitleEdit.Core.SubtitleFormats; + +namespace Nikse.SubtitleEdit.Core +{ + public class TimeCode + { + public static readonly TimeCode MaxTime = new TimeCode(99, 59, 59, 999); + + public const double BaseUnit = 1000.0; // Base unit of time + private double _totalMilliseconds; + + public bool IsMaxTime + { + get + { + return Math.Abs(_totalMilliseconds - MaxTime.TotalMilliseconds) < 0.01; + } + } + + public static TimeCode FromSeconds(double seconds) + { + return new TimeCode(seconds * BaseUnit); + } + + public static double ParseToMilliseconds(string text) + { + string[] parts = text.Split(new[] { ':', ',', '.' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 4) + { + int hours; + int minutes; + int seconds; + int milliseconds; + if (int.TryParse(parts[0], out hours) && int.TryParse(parts[1], out minutes) && int.TryParse(parts[2], out seconds) && int.TryParse(parts[3], out milliseconds)) + { + return new TimeSpan(0, hours, minutes, seconds, milliseconds).TotalMilliseconds; + } + } + return 0; + } + + public static double ParseHHMMSSFFToMilliseconds(string text) + { + string[] parts = text.Split(new[] { ':', ',', '.' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 4) + { + int hours; + int minutes; + int seconds; + int frames; + if (int.TryParse(parts[0], out hours) && int.TryParse(parts[1], out minutes) && int.TryParse(parts[2], out seconds) && int.TryParse(parts[3], out frames)) + { + return new TimeCode(hours, minutes, seconds, SubtitleFormat.FramesToMillisecondsMax999(frames)).TotalMilliseconds; + } + } + return 0; + } + + public TimeCode(TimeSpan timeSpan) + { + _totalMilliseconds = timeSpan.TotalMilliseconds; + } + + public TimeCode(double totalMilliseconds) + { + _totalMilliseconds = totalMilliseconds; + } + + public TimeCode(int hour, int minute, int seconds, int milliseconds) + { + _totalMilliseconds = hour * 60 * 60 * BaseUnit + minute * 60 * BaseUnit + seconds * BaseUnit + milliseconds; + } + + public int Hours + { + get + { + var ts = TimeSpan; + return ts.Hours + ts.Days * 24; + } + set + { + var ts = TimeSpan; + _totalMilliseconds = new TimeSpan(0, value, ts.Minutes, ts.Seconds, ts.Milliseconds).TotalMilliseconds; + } + } + + public int Minutes + { + get + { + return TimeSpan.Minutes; + } + set + { + var ts = TimeSpan; + _totalMilliseconds = new TimeSpan(0, ts.Hours, value, ts.Seconds, ts.Milliseconds).TotalMilliseconds; + } + } + + public int Seconds + { + get + { + return TimeSpan.Seconds; + } + set + { + var ts = TimeSpan; + _totalMilliseconds = new TimeSpan(0, ts.Hours, ts.Minutes, value, ts.Milliseconds).TotalMilliseconds; + } + } + + public int Milliseconds + { + get + { + return TimeSpan.Milliseconds; + } + set + { + var ts = TimeSpan; + _totalMilliseconds = new TimeSpan(0, ts.Hours, ts.Minutes, ts.Seconds, value).TotalMilliseconds; + } + } + + public double TotalMilliseconds + { + get { return _totalMilliseconds; } + set { _totalMilliseconds = value; } + } + + public double TotalSeconds + { + get { return _totalMilliseconds / BaseUnit; } + set { _totalMilliseconds = value * BaseUnit; } + } + + public TimeSpan TimeSpan + { + get + { + return TimeSpan.FromMilliseconds(_totalMilliseconds); + } + set + { + _totalMilliseconds = value.TotalMilliseconds; + } + } + + public void AddTime(int hour, int minutes, int seconds, int milliseconds) + { + Hours += hour; + Minutes += minutes; + Seconds += seconds; + Milliseconds += milliseconds; + } + + public void AddTime(long milliseconds) + { + _totalMilliseconds += milliseconds; + } + + public void AddTime(TimeSpan timeSpan) + { + _totalMilliseconds += timeSpan.TotalMilliseconds; + } + + public void AddTime(double milliseconds) + { + _totalMilliseconds += milliseconds; + } + + public override string ToString() + { + var ts = TimeSpan; + string s = string.Format("{0:00}:{1:00}:{2:00},{3:000}", ts.Hours + ts.Days * 24, ts.Minutes, ts.Seconds, ts.Milliseconds); + + if (TotalMilliseconds >= 0) + return s; + return "-" + s.Replace("-", string.Empty); + } + + public string ToShortString() + { + var ts = TimeSpan; + string s; + if (ts.Minutes == 0 && ts.Hours == 0 && ts.Days == 0) + s = string.Format("{0:0},{1:000}", ts.Seconds, ts.Milliseconds); + else if (ts.Hours == 0 && ts.Days == 0) + s = string.Format("{0:0}:{1:00},{2:000}", ts.Minutes, ts.Seconds, ts.Milliseconds); + else + s = string.Format("{0:0}:{1:00}:{2:00},{3:000}", ts.Hours + ts.Days * 24, ts.Minutes, ts.Seconds, ts.Milliseconds); + + if (TotalMilliseconds >= 0) + return s; + return "-" + s.Replace("-", string.Empty); + } + + public string ToShortStringHHMMSSFF() + { + var ts = TimeSpan; + if (ts.Minutes == 0 && ts.Hours == 0) + return string.Format("{0:00}:{1:00}", ts.Seconds, SubtitleFormat.MillisecondsToFramesMaxFrameRate(ts.Milliseconds)); + if (ts.Hours == 0) + return string.Format("{0:00}:{1:00}:{2:00}", ts.Minutes, ts.Seconds, SubtitleFormat.MillisecondsToFramesMaxFrameRate(ts.Milliseconds)); + return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", ts.Hours, ts.Minutes, ts.Seconds, SubtitleFormat.MillisecondsToFramesMaxFrameRate(ts.Milliseconds)); + } + + public string ToHHMMSSFF() + { + var ts = TimeSpan; + return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", ts.Hours, ts.Minutes, ts.Seconds, SubtitleFormat.MillisecondsToFramesMaxFrameRate(ts.Milliseconds)); + } + + public string ToHHMMSSPeriodFF() + { + var ts = TimeSpan; + return string.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, SubtitleFormat.MillisecondsToFramesMaxFrameRate(ts.Milliseconds)); + } + + } +} \ No newline at end of file diff --git a/libse/TransportStream/AdaptationField.cs b/libse/TransportStream/AdaptationField.cs new file mode 100644 index 000000000..7725e53cf --- /dev/null +++ b/libse/TransportStream/AdaptationField.cs @@ -0,0 +1,108 @@ +using System; + +namespace Nikse.SubtitleEdit.Core.TransportStream +{ + public class AdaptationField + { + /// + /// Number of bytes in the adaptation field immediately following the Length + /// + public int Length { get; set; } + + public bool DiscontinuityIndicator { get; set; } + public bool RandomAccessIndicator { get; set; } + public bool ElementaryStreamPriorityIndicator { get; set; } + + /// + /// '1' indicates that the adaptation field contains a PCR field coded in two parts + /// + public bool PcrFlag { get; set; } + + /// + /// '1' indicates that the adaptation field contains an OPCR field coded in two parts + /// + public bool OpcrFlag { get; set; } + + /// + /// '1' indicates that a splice countdown field shall be present in the associated adaptation field + /// + public bool SplicingPointFlag { get; set; } + + /// + /// 1' indicates that the adaptation field contains one or more private data bytes + /// + public bool TransportPrivateDataFlag { get; set; } + + /// + /// '1' indicates the presence of an adaptation field extension + /// + public bool AdaptationFieldExtensionFlag { get; set; } + + public int ProgramClockReferenceBase { get; set; } + public int ProgramClockReferenceExtension { get; set; } + + public int OriginalProgramClockReferenceBase { get; set; } + public int OriginalProgramClockReferenceExtension { get; set; } + + public int SpliceCountdown { get; set; } + + public int TransportPrivateDataLength { get; set; } + public byte[] TransportPrivateData { get; set; } + + public int AdaptationFieldExtensionLength { get; set; } + + public AdaptationField(byte[] packetBuffer) + { + Length = packetBuffer[4]; + DiscontinuityIndicator = 1 == packetBuffer[5] >> 7; + RandomAccessIndicator = (packetBuffer[5] & 64) > 0; // and with 01000000 to get second byte + ElementaryStreamPriorityIndicator = (packetBuffer[5] & 32) > 0; // and with 00100000 to get third byte + PcrFlag = (packetBuffer[5] & 16) > 0; // and with 00010000 to get fourth byte + OpcrFlag = (packetBuffer[5] & 8) > 0; // and with 00001000 to get fifth byte + SplicingPointFlag = (packetBuffer[5] & 4) > 0; // and with 00000100 to get sixth byte + TransportPrivateDataFlag = (packetBuffer[5] & 4) > 0; // and with 00000100 to get seventh byte + AdaptationFieldExtensionFlag = (packetBuffer[5] & 2) > 0; // and with 00000010 to get 8th byte + + int index = 6; + if (PcrFlag) + { + ProgramClockReferenceBase = (packetBuffer[index] * 256 + packetBuffer[index + 1]) << 1; + ProgramClockReferenceBase += packetBuffer[index + 2] >> 7; + ProgramClockReferenceExtension = (packetBuffer[index + 2] & Helper.B00000001) * 256 + packetBuffer[index + 3]; + index += 4; + } + + if (OpcrFlag) + { + OriginalProgramClockReferenceBase = (packetBuffer[index] * 256 + packetBuffer[index + 1]) << 1; + OriginalProgramClockReferenceBase += packetBuffer[index + 2] >> 7; + OriginalProgramClockReferenceExtension = (packetBuffer[index + 2] & Helper.B00000001) * 256 + packetBuffer[index + 3]; + index += 4; + } + + if (SplicingPointFlag) + { + SpliceCountdown = packetBuffer[index]; + index++; + } + + if (TransportPrivateDataFlag) + { + TransportPrivateDataLength = packetBuffer[index]; + index++; + TransportPrivateData = new byte[TransportPrivateDataLength]; + + if (index + TransportPrivateDataLength <= packetBuffer.Length) + { + Buffer.BlockCopy(packetBuffer, index, TransportPrivateData, 0, TransportPrivateDataLength); + index += TransportPrivateDataLength; + } + } + + if (AdaptationFieldExtensionFlag && index < packetBuffer.Length) + { + AdaptationFieldExtensionLength = packetBuffer[index]; + } + } + } +} \ No newline at end of file diff --git a/libse/TransportStream/ClutDefinitionSegment.cs b/libse/TransportStream/ClutDefinitionSegment.cs new file mode 100644 index 000000000..e077afcde --- /dev/null +++ b/libse/TransportStream/ClutDefinitionSegment.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; + +namespace Nikse.SubtitleEdit.Core.TransportStream +{ + public class ClutDefinitionSegment + { + public int ClutId { get; set; } + public int ClutVersionNumber { get; set; } + public List Entries = new List(); + + public ClutDefinitionSegment(byte[] buffer, int index, int segmentLength) + { + Entries = new List(); + ClutId = buffer[index]; + ClutVersionNumber = buffer[index + 1] >> 4; + + int k = index + 2; + while (k + 6 <= index + segmentLength) + { + var rcse = new RegionClutSegmentEntry(); + rcse.ClutEntryId = buffer[k]; + byte flags = buffer[k + 1]; + + rcse.ClutEntry2BitClutEntryFlag = (flags & Helper.B10000000) > 0; + rcse.ClutEntry4BitClutEntryFlag = (flags & Helper.B01000000) > 0; + rcse.ClutEntry8BitClutEntryFlag = (flags & Helper.B00100000) > 0; + rcse.FullRangeFlag = (flags & Helper.B00000001) > 0; + + if (rcse.FullRangeFlag) + { + rcse.ClutEntryY = buffer[k + 2]; + rcse.ClutEntryCr = buffer[k + 3]; + rcse.ClutEntryCb = buffer[k + 4]; + rcse.ClutEntryT = buffer[k + 5]; + k += 6; + } + else + { + rcse.ClutEntryY = buffer[k + 2] >> 2; + rcse.ClutEntryCr = ((buffer[k + 2] & Helper.B00000011) << 2) + (buffer[k + 2]) >> 6; + rcse.ClutEntryCb = ((buffer[k + 3] & Helper.B00111111) >> 2); + rcse.ClutEntryT = buffer[k + 3] & Helper.B00000011; + k += 4; + } + Entries.Add(rcse); + } + } + } +} \ No newline at end of file diff --git a/libse/TransportStream/DisplayDefinitionSegment.cs b/libse/TransportStream/DisplayDefinitionSegment.cs new file mode 100644 index 000000000..426b4c103 --- /dev/null +++ b/libse/TransportStream/DisplayDefinitionSegment.cs @@ -0,0 +1,30 @@ +namespace Nikse.SubtitleEdit.Core.TransportStream +{ + public class DisplayDefinitionSegment + { + public int DisplayDefinitionVersionNumber { get; set; } + public bool DisplayWindowFlag { get; set; } + public int DisplayWith { get; set; } + public int DisplayHeight { get; set; } + public int? DisplayWindowHorizontalPositionMinimum { get; set; } + public int? DisplayWindowHorizontalPositionMaximum { get; set; } + public int? DisplayWindowVerticalPositionMinimum { get; set; } + public int? DisplayWindowVerticalPositionMaximum { get; set; } + + public DisplayDefinitionSegment(byte[] buffer, int index) + { + DisplayDefinitionVersionNumber = buffer[index] >> 4; + DisplayWindowFlag = (buffer[index] & Helper.B00001000) > 0; + DisplayWith = Helper.GetEndianWord(buffer, index + 1); + DisplayHeight = Helper.GetEndianWord(buffer, index + 3); + if (DisplayWindowFlag) + { + DisplayWindowHorizontalPositionMinimum = Helper.GetEndianWord(buffer, index + 5); + DisplayWindowHorizontalPositionMaximum = Helper.GetEndianWord(buffer, index + 7); + DisplayWindowVerticalPositionMinimum = Helper.GetEndianWord(buffer, index + 9); + DisplayWindowVerticalPositionMaximum = Helper.GetEndianWord(buffer, index + 11); + } + } + + } +} \ No newline at end of file diff --git a/libse/TransportStream/DvbSubPes.cs b/libse/TransportStream/DvbSubPes.cs new file mode 100644 index 000000000..f33477d0b --- /dev/null +++ b/libse/TransportStream/DvbSubPes.cs @@ -0,0 +1,395 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; + +namespace Nikse.SubtitleEdit.Core.TransportStream +{ + + public class DvbSubPes + { + public const int HeaderLength = 6; + public const int Mpeg2HeaderLength = 14; + + public readonly UInt32 StartCode; + public readonly int StreamId; + public readonly int Length; + public readonly int ScramblingControl; + public readonly int Priority; + public readonly int DataAlignmentIndicator; + public readonly int Copyright; + public readonly int OriginalOrCopy; + public readonly int PresentationTimestampDecodeTimestampFlags; + public readonly int ElementaryStreamClockReferenceFlag; + public readonly int EsRateFlag; + public readonly int DsmTrickModeFlag; + public readonly int AdditionalCopyInfoFlag; + public readonly int CrcFlag; + public readonly int ExtensionFlag; + public readonly int HeaderDataLength; + + public readonly UInt64? PresentationTimestamp; + public readonly UInt64? DecodeTimestamp; + + public readonly int? SubPictureStreamId; + + private readonly byte[] _dataBuffer; + + public DvbSubPes(byte[] buffer, int index) + { + if (buffer.Length < 9) + return; + + StartCode = Helper.GetEndian(buffer, index + 0, 3); + StreamId = buffer[index + 3]; + Length = Helper.GetEndianWord(buffer, index + 4); + + ScramblingControl = (buffer[index + 6] >> 4) & Helper.B00000011; + Priority = buffer[index + 6] & Helper.B00001000; + DataAlignmentIndicator = buffer[index + 6] & Helper.B00000100; + Copyright = buffer[index + 6] & Helper.B00000010; + OriginalOrCopy = buffer[index + 6] & Helper.B00000001; + PresentationTimestampDecodeTimestampFlags = buffer[index + 7] >> 6; + ElementaryStreamClockReferenceFlag = buffer[index + 7] & Helper.B00100000; + EsRateFlag = buffer[index + 7] & Helper.B00010000; + DsmTrickModeFlag = buffer[index + 7] & Helper.B00001000; + AdditionalCopyInfoFlag = buffer[index + 7] & Helper.B00000100; + CrcFlag = buffer[index + 7] & Helper.B00001000; + ExtensionFlag = buffer[index + 7] & Helper.B00000010; + + HeaderDataLength = buffer[index + 8]; + + if (StreamId == 0xBD) // 10111101 binary = 189 decimal = 0xBD hex -> private_stream_1 + { + int id = buffer[index + 9 + HeaderDataLength]; + if (id >= 0x20 && id <= 0x40) // x3f 0r x40 ? + SubPictureStreamId = id; + } + + int tempIndex = index + 9; + if (PresentationTimestampDecodeTimestampFlags == Helper.B00000010 || + PresentationTimestampDecodeTimestampFlags == Helper.B00000011) + { + PresentationTimestamp = (ulong)buffer[tempIndex + 4] >> 1; + PresentationTimestamp += (ulong)buffer[tempIndex + 3] << 7; + PresentationTimestamp += (ulong)(buffer[tempIndex + 2] & Helper.B11111110) << 14; + PresentationTimestamp += (ulong)buffer[tempIndex + 1] << 22; + PresentationTimestamp += (ulong)(buffer[tempIndex + 0] & Helper.B00001110) << 29; + } + if (PresentationTimestampDecodeTimestampFlags == Helper.B00000011) + { + DecodeTimestamp = (ulong)buffer[tempIndex + 4] >> 1; + DecodeTimestamp += (ulong)buffer[tempIndex + 3] << 7; + DecodeTimestamp += (ulong)(buffer[tempIndex + 2] & Helper.B11111110) << 14; + DecodeTimestamp += (ulong)buffer[tempIndex + 1] << 22; + DecodeTimestamp += (ulong)(buffer[tempIndex + 0] & Helper.B00001110) << 29; + } + + int dataIndex = index + HeaderDataLength + 24 - Mpeg2HeaderLength; + int dataSize = Length - (4 + HeaderDataLength); + + if (dataSize < 0 || (dataSize + dataIndex > buffer.Length)) // to fix bad subs... + { + dataSize = buffer.Length - dataIndex; + if (dataSize < 0) + return; + } + + _dataBuffer = new byte[dataSize + 1]; + Buffer.BlockCopy(buffer, dataIndex - 1, _dataBuffer, 0, _dataBuffer.Length); // why subtract one from dataIndex??? + } + + public DvbSubPes(int index, byte[] buffer) + { + int start = index; + Length = index + 1; + + if (index + 9 >= buffer.Length) + return; + + if (buffer[0 + index] != 0x20) + return; + + if (buffer[1 + index] != 0) + return; + + SubtitleSegments = new List(); + ClutDefinitions = new List(); + RegionCompositions = new List(); + PageCompositions = new List(); + ObjectDataList = new List(); + + // Find length of segments + index = start + 2; + var ss = new SubtitleSegment(buffer, index); + while (ss.SyncByte == Helper.B00001111) + { + SubtitleSegments.Add(ss); + index += 6 + ss.SegmentLength; + if (index + 6 < buffer.Length) + ss = new SubtitleSegment(buffer, index); + else + ss.SyncByte = Helper.B11111111; + } + Length = index; + int size = index - start; + _dataBuffer = new byte[size]; + Buffer.BlockCopy(buffer, start, _dataBuffer, 0, _dataBuffer.Length); + + // Parse segments + index = 2; + ss = new SubtitleSegment(_dataBuffer, index); + while (ss.SyncByte == Helper.B00001111) + { + SubtitleSegments.Add(ss); + if (ss.ClutDefinition != null) + { + ClutDefinitions.Add(ss.ClutDefinition); + } + else if (ss.RegionComposition != null) + { + RegionCompositions.Add(ss.RegionComposition); + } + else if (ss.PageComposition != null) + { + PageCompositions.Add(ss.PageComposition); + } + else if (ss.ObjectData != null) + { + ObjectDataList.Add(ss.ObjectData); + } + + index += 6 + ss.SegmentLength; + if (index + 6 < _dataBuffer.Length) + ss = new SubtitleSegment(_dataBuffer, index); + else + ss.SyncByte = Helper.B11111111; + } + } + + public bool IsDvbSubpicture + { + get { return SubPictureStreamId.HasValue && SubPictureStreamId.Value == 32; } + } + + public int DataIdentifier + { + get + { + if (_dataBuffer == null || _dataBuffer.Length < 2) + return 0; + + return _dataBuffer[0]; + } + } + + public int SubtitleStreamId + { + get + { + if (_dataBuffer == null || _dataBuffer.Length < 2) + return 0; + + return _dataBuffer[1]; + } + } + + public List SubtitleSegments { get; set; } + public List ClutDefinitions { get; set; } + public List RegionCompositions { get; set; } + public List PageCompositions { get; set; } + public List ObjectDataList { get; set; } + + public void ParseSegments() + { + if (SubtitleSegments != null) + return; + + SubtitleSegments = new List(); + ClutDefinitions = new List(); + RegionCompositions = new List(); + PageCompositions = new List(); + ObjectDataList = new List(); + + int index = 2; + var ss = new SubtitleSegment(_dataBuffer, index); + while (ss.SyncByte == Helper.B00001111) + { + SubtitleSegments.Add(ss); + if (ss.ClutDefinition != null) + { + ClutDefinitions.Add(ss.ClutDefinition); + } + else if (ss.RegionComposition != null) + { + RegionCompositions.Add(ss.RegionComposition); + } + else if (ss.PageComposition != null) + { + PageCompositions.Add(ss.PageComposition); + } + else if (ss.ObjectData != null) + { + ObjectDataList.Add(ss.ObjectData); + } + + index += 6 + ss.SegmentLength; + if (index + 6 < _dataBuffer.Length) + ss = new SubtitleSegment(_dataBuffer, index); + else + ss.SyncByte = Helper.B11111111; + } + } + + private ClutDefinitionSegment GetClutDefinitionSegment(ObjectDataSegment ods) + { + foreach (RegionCompositionSegment rcs in RegionCompositions) + { + foreach (RegionCompositionSegmentObject o in rcs.Objects) + { + if (o.ObjectId == ods.ObjectId) + { + foreach (ClutDefinitionSegment cds in ClutDefinitions) + { + if (cds.ClutId == rcs.RegionClutId) + return cds; + } + } + } + } + + if (ClutDefinitions.Count > 0) + return ClutDefinitions[0]; + + return null; // TODO: Return default clut + } + + public Point GetImagePosition(ObjectDataSegment ods) + { + if (SubtitleSegments == null) + ParseSegments(); + + var p = new Point(0, 0); + + foreach (RegionCompositionSegment rcs in RegionCompositions) + { + foreach (RegionCompositionSegmentObject o in rcs.Objects) + { + if (o.ObjectId == ods.ObjectId) + { + foreach (PageCompositionSegment cds in PageCompositions) + { + foreach (var r in cds.Regions) + { + if (r.RegionId == rcs.RegionId) + { + p.X = r.RegionHorizontalAddress + o.ObjectHorizontalPosition; + p.Y = r.RegionVerticalAddress + o.ObjectVerticalPosition; + return p; + } + } + } + p.X = o.ObjectHorizontalPosition; + p.Y = o.ObjectVerticalPosition; + } + } + } + + return p; + } + + public Bitmap GetImage(ObjectDataSegment ods) + { + if (SubtitleSegments == null) + ParseSegments(); + + if (ods.Image != null) + return ods.Image; + + ClutDefinitionSegment cds = GetClutDefinitionSegment(ods); + ods.DecodeImage(_dataBuffer, ods.BufferIndex, cds); + return ods.Image; + } + + public Bitmap GetImageFull() + { + if (SubtitleSegments == null) + ParseSegments(); + + int width = 720; + int height = 576; + + var segments = SubtitleSegments; + foreach (SubtitleSegment ss in segments) + { + if (ss.DisplayDefinition != null) + { + width = ss.DisplayDefinition.DisplayWith; + height = ss.DisplayDefinition.DisplayHeight; + } + } + + var bmp = new Bitmap(width, height); + foreach (var ods in ObjectDataList) + { + var odsImage = GetImage(ods); + if (odsImage != null) + { + var odsPoint = GetImagePosition(ods); + using (var g = Graphics.FromImage(bmp)) + { + g.DrawImageUnscaled(odsImage, odsPoint); + } + } + } + return bmp; + } + + public static string GetStreamIdDescription(int streamId) + { + if (0xC0 <= streamId && streamId < 0xE0) + return "ISO/IEC 13818-3 or ISO/IEC 11172-3 or ISO/IEC 13818-7 or ISO/IEC 14496-3 audio stream number " + (streamId & 0x1F).ToString("X4"); + + if (0xE0 <= streamId && streamId < 0xF0) + return "ITU-T Rec. H.262 | ISO/IEC 13818-2 or ISO/IEC 11172-2 or ISO/IEC 14496-2 video stream number " + (streamId & 0x0F).ToString("X4"); + + switch (streamId) + { + case 0xBC: return "program_stream_map"; + case 0xBD: return "private_stream_1"; + case 0xBE: return "padding_stream"; + case 0xBF: return "private_stream_2"; + case 0xF0: return "ECM_stream"; + case 0xF1: return "EMM_stream"; + case 0xF2: return "DSMCC_stream"; + case 0xF3: return "ISO/IEC_13522_stream"; + case 0xF4: return "ITU-T Rec. H.222.1 type A"; + case 0xF5: return "ITU-T Rec. H.222.1 type B"; + case 0xF6: return "ITU-T Rec. H.222.1 type C"; + case 0xF7: return "ITU-T Rec. H.222.1 type D"; + case 0xF8: return "ITU-T Rec. H.222.1 type E"; + case 0xF9: return "ancillary_stream"; + case 0xFA: return "ISO/IEC14496-1_SL-packetized_stream"; + case 0xFB: return "ISO/IEC14496-1_FlexMux_stream"; + case 0xFC: return "metadata stream"; + case 0xFD: return "extended_stream_id"; + case 0xFE: return "reserved data stream"; + case 0xFF: return "program_stream_directory"; + default: return "?"; + } + } + + public ulong PresentationTimestampToMilliseconds() + { + if (PresentationTimestamp.HasValue) + return (ulong)Math.Round((PresentationTimestamp.Value + 45.0) / 90.0); + return 0; + } + + public void WriteToStream(Stream stream) + { + stream.Write(_dataBuffer, 0, _dataBuffer.Length); + } + + } +} \ No newline at end of file diff --git a/libse/TransportStream/EbuPesDataField.cs b/libse/TransportStream/EbuPesDataField.cs new file mode 100644 index 000000000..4d2939f07 --- /dev/null +++ b/libse/TransportStream/EbuPesDataField.cs @@ -0,0 +1,22 @@ +namespace Nikse.SubtitleEdit.Core.TransportStream +{ + + public class EbuPesDataFieldText + { + public bool FieldParity { get; set; } + public int LineOffset { get; set; } + public int FramingCode { get; set; } + public int MagazineAndPacketAddress { get; set; } + public byte[] DataBlock { get; set; } + + public EbuPesDataFieldText(byte[] buffer, int index) + { + FieldParity = (buffer[index] & Helper.B00100000) > 0; + LineOffset = buffer[index] & Helper.B00011111; + FramingCode = buffer[index + 1]; + MagazineAndPacketAddress = Helper.GetEndianWord(buffer, index + 2); + } + + } + +} diff --git a/libse/TransportStream/EbuPesDataFieldText.cs b/libse/TransportStream/EbuPesDataFieldText.cs new file mode 100644 index 000000000..4a3ac6b82 --- /dev/null +++ b/libse/TransportStream/EbuPesDataFieldText.cs @@ -0,0 +1,10 @@ +namespace Nikse.SubtitleEdit.Core.TransportStream +{ + public class EbuPesDataField + { + public int DataUnitId { get; set; } + public int DataUnitLength { get; set; } + public byte[] DataField { get; set; } + public EbuPesDataFieldText FieldText; + } +} \ No newline at end of file diff --git a/libse/TransportStream/Helper.cs b/libse/TransportStream/Helper.cs new file mode 100644 index 000000000..be8255ab3 --- /dev/null +++ b/libse/TransportStream/Helper.cs @@ -0,0 +1,330 @@ +using System; +using System.Text; + +namespace Nikse.SubtitleEdit.Core.TransportStream +{ + public static class Helper + { + + #region Binary constants + + public const int B00000000 = 0; + public const int B00000001 = 1; + public const int B00000010 = 2; + public const int B00000011 = 3; + public const int B00000100 = 4; + public const int B00000101 = 5; + public const int B00000110 = 6; + public const int B00000111 = 7; + public const int B00001000 = 8; + public const int B00001001 = 9; + public const int B00001010 = 10; + public const int B00001011 = 11; + public const int B00001100 = 12; + public const int B00001101 = 13; + public const int B00001110 = 14; + public const int B00001111 = 15; + public const int B00010000 = 16; + public const int B00010001 = 17; + public const int B00010010 = 18; + public const int B00010011 = 19; + public const int B00010100 = 20; + public const int B00010101 = 21; + public const int B00010110 = 22; + public const int B00010111 = 23; + public const int B00011000 = 24; + public const int B00011001 = 25; + public const int B00011010 = 26; + public const int B00011011 = 27; + public const int B00011100 = 28; + public const int B00011101 = 29; + public const int B00011110 = 30; + public const int B00011111 = 31; + public const int B00100000 = 32; + public const int B00100001 = 33; + public const int B00100010 = 34; + public const int B00100011 = 35; + public const int B00100100 = 36; + public const int B00100101 = 37; + public const int B00100110 = 38; + public const int B00100111 = 39; + public const int B00101000 = 40; + public const int B00101001 = 41; + public const int B00101010 = 42; + public const int B00101011 = 43; + public const int B00101100 = 44; + public const int B00101101 = 45; + public const int B00101110 = 46; + public const int B00101111 = 47; + public const int B00110000 = 48; + public const int B00110001 = 49; + public const int B00110010 = 50; + public const int B00110011 = 51; + public const int B00110100 = 52; + public const int B00110101 = 53; + public const int B00110110 = 54; + public const int B00110111 = 55; + public const int B00111000 = 56; + public const int B00111001 = 57; + public const int B00111010 = 58; + public const int B00111011 = 59; + public const int B00111100 = 60; + public const int B00111101 = 61; + public const int B00111110 = 62; + public const int B00111111 = 63; + public const int B01000000 = 64; + public const int B01000001 = 65; + public const int B01000010 = 66; + public const int B01000011 = 67; + public const int B01000100 = 68; + public const int B01000101 = 69; + public const int B01000110 = 70; + public const int B01000111 = 71; + public const int B01001000 = 72; + public const int B01001001 = 73; + public const int B01001010 = 74; + public const int B01001011 = 75; + public const int B01001100 = 76; + public const int B01001101 = 77; + public const int B01001110 = 78; + public const int B01001111 = 79; + public const int B01010000 = 80; + public const int B01010001 = 81; + public const int B01010010 = 82; + public const int B01010011 = 83; + public const int B01010100 = 84; + public const int B01010101 = 85; + public const int B01010110 = 86; + public const int B01010111 = 87; + public const int B01011000 = 88; + public const int B01011001 = 89; + public const int B01011010 = 90; + public const int B01011011 = 91; + public const int B01011100 = 92; + public const int B01011101 = 93; + public const int B01011110 = 94; + public const int B01011111 = 95; + public const int B01100000 = 96; + public const int B01100001 = 97; + public const int B01100010 = 98; + public const int B01100011 = 99; + public const int B01100100 = 100; + public const int B01100101 = 101; + public const int B01100110 = 102; + public const int B01100111 = 103; + public const int B01101000 = 104; + public const int B01101001 = 105; + public const int B01101010 = 106; + public const int B01101011 = 107; + public const int B01101100 = 108; + public const int B01101101 = 109; + public const int B01101110 = 110; + public const int B01101111 = 111; + public const int B01110000 = 112; + public const int B01110001 = 113; + public const int B01110010 = 114; + public const int B01110011 = 115; + public const int B01110100 = 116; + public const int B01110101 = 117; + public const int B01110110 = 118; + public const int B01110111 = 119; + public const int B01111000 = 120; + public const int B01111001 = 121; + public const int B01111010 = 122; + public const int B01111011 = 123; + public const int B01111100 = 124; + public const int B01111101 = 125; + public const int B01111110 = 126; + public const int B01111111 = 127; + public const int B10000000 = 128; + public const int B10000001 = 129; + public const int B10000010 = 130; + public const int B10000011 = 131; + public const int B10000100 = 132; + public const int B10000101 = 133; + public const int B10000110 = 134; + public const int B10000111 = 135; + public const int B10001000 = 136; + public const int B10001001 = 137; + public const int B10001010 = 138; + public const int B10001011 = 139; + public const int B10001100 = 140; + public const int B10001101 = 141; + public const int B10001110 = 142; + public const int B10001111 = 143; + public const int B10010000 = 144; + public const int B10010001 = 145; + public const int B10010010 = 146; + public const int B10010011 = 147; + public const int B10010100 = 148; + public const int B10010101 = 149; + public const int B10010110 = 150; + public const int B10010111 = 151; + public const int B10011000 = 152; + public const int B10011001 = 153; + public const int B10011010 = 154; + public const int B10011011 = 155; + public const int B10011100 = 156; + public const int B10011101 = 157; + public const int B10011110 = 158; + public const int B10011111 = 159; + public const int B10100000 = 160; + public const int B10100001 = 161; + public const int B10100010 = 162; + public const int B10100011 = 163; + public const int B10100100 = 164; + public const int B10100101 = 165; + public const int B10100110 = 166; + public const int B10100111 = 167; + public const int B10101000 = 168; + public const int B10101001 = 169; + public const int B10101010 = 170; + public const int B10101011 = 171; + public const int B10101100 = 172; + public const int B10101101 = 173; + public const int B10101110 = 174; + public const int B10101111 = 175; + public const int B10110000 = 176; + public const int B10110001 = 177; + public const int B10110010 = 178; + public const int B10110011 = 179; + public const int B10110100 = 180; + public const int B10110101 = 181; + public const int B10110110 = 182; + public const int B10110111 = 183; + public const int B10111000 = 184; + public const int B10111001 = 185; + public const int B10111010 = 186; + public const int B10111011 = 187; + public const int B10111100 = 188; + public const int B10111101 = 189; + public const int B10111110 = 190; + public const int B10111111 = 191; + public const int B11000000 = 192; + public const int B11000001 = 193; + public const int B11000010 = 194; + public const int B11000011 = 195; + public const int B11000100 = 196; + public const int B11000101 = 197; + public const int B11000110 = 198; + public const int B11000111 = 199; + public const int B11001000 = 200; + public const int B11001001 = 201; + public const int B11001010 = 202; + public const int B11001011 = 203; + public const int B11001100 = 204; + public const int B11001101 = 205; + public const int B11001110 = 206; + public const int B11001111 = 207; + public const int B11010000 = 208; + public const int B11010001 = 209; + public const int B11010010 = 210; + public const int B11010011 = 211; + public const int B11010100 = 212; + public const int B11010101 = 213; + public const int B11010110 = 214; + public const int B11010111 = 215; + public const int B11011000 = 216; + public const int B11011001 = 217; + public const int B11011010 = 218; + public const int B11011011 = 219; + public const int B11011100 = 220; + public const int B11011101 = 221; + public const int B11011110 = 222; + public const int B11011111 = 223; + public const int B11100000 = 224; + public const int B11100001 = 225; + public const int B11100010 = 226; + public const int B11100011 = 227; + public const int B11100100 = 228; + public const int B11100101 = 229; + public const int B11100110 = 230; + public const int B11100111 = 231; + public const int B11101000 = 232; + public const int B11101001 = 233; + public const int B11101010 = 234; + public const int B11101011 = 235; + public const int B11101100 = 236; + public const int B11101101 = 237; + public const int B11101110 = 238; + public const int B11101111 = 239; + public const int B11110000 = 240; + public const int B11110001 = 241; + public const int B11110010 = 242; + public const int B11110011 = 243; + public const int B11110100 = 244; + public const int B11110101 = 245; + public const int B11110110 = 246; + public const int B11110111 = 247; + public const int B11111000 = 248; + public const int B11111001 = 249; + public const int B11111010 = 250; + public const int B11111011 = 251; + public const int B11111100 = 252; + public const int B11111101 = 253; + public const int B11111110 = 254; + public const int B11111111 = 255; + + #endregion Binary constants + + public static string IntToHex(UInt64 value, int digits) + { + return value.ToString("X").PadLeft(digits, '0'); + } + + public static string IntToHex(int value, int digits) + { + return value.ToString("X").PadLeft(digits, '0'); + } + + public static string IntToBin(long value, int digits) + { + return Convert.ToString(value, 2).PadLeft(digits, '0'); + } + + public static UInt32 GetEndian(byte[] buffer, int index, int count) + { + UInt32 result = 0; + for (int i = 0; i < count; i++) + result = (result << 8) + buffer[index + i]; + return result; + } + + public static int GetLittleEndian32(byte[] buffer, int index) + { + return (buffer[index + 3] << 24 | (int)buffer[index + 2] << 16 | (int)buffer[index + 1] << 8 | (int)buffer[index + 0]); + } + + //private int Swap4Bytes(byte[] b) + //{ + // return ((int)b[3] << 24 | (int)b[2] << 16 | (int)b[1] << 8 | (int)b[0]); + //} + + /// + /// Get two bytes word stored in endian order + /// + /// Byte array + /// Index in byte array + /// Word as int + public static int GetEndianWord(byte[] buffer, int index) + { + if (index + 1 < buffer.Length) + return (buffer[index] << 8) + buffer[index + 1]; + return 0; + } + + public static string GetBinaryString(byte[] buffer, int index, int count) + { + var sb = new StringBuilder(); + for (int i = 0; i < count; i++) + sb.Append(Convert.ToString(buffer[index + i], 2).PadLeft(8, '0')); + return sb.ToString(); + } + + public static UInt64 GetUInt32FromBinaryString(string binaryValue) + { + return Convert.ToUInt32(binaryValue, 2); + } + + } +} \ No newline at end of file diff --git a/libse/TransportStream/HummingDecoder.cs b/libse/TransportStream/HummingDecoder.cs new file mode 100644 index 000000000..6e39f81f4 --- /dev/null +++ b/libse/TransportStream/HummingDecoder.cs @@ -0,0 +1,49 @@ +namespace Nikse.SubtitleEdit.Core.TransportStream +{ + internal class HummingDecoder + { + private static byte[] _unhammingTable = new byte[256] + { + 0x01, 0xff, 0x81, 0x01, 0xff, 0x00, 0x01, 0xff, 0xff, 0x02, 0x01, 0xff, 0x0a, 0xff, 0xff, 0x07, + 0xff, 0x00, 0x01, 0xff, 0x00, 0x80, 0xff, 0x00, 0x06, 0xff, 0xff, 0x0b, 0xff, 0x00, 0x03, 0xff, + 0xff, 0x0c, 0x01, 0xff, 0x04, 0xff, 0xff, 0x07, 0x06, 0xff, 0xff, 0x07, 0xff, 0x07, 0x07, 0x87, + 0x06, 0xff, 0xff, 0x05, 0xff, 0x00, 0x0d, 0xff, 0x86, 0x06, 0x06, 0xff, 0x06, 0xff, 0xff, 0x07, + 0xff, 0x02, 0x01, 0xff, 0x04, 0xff, 0xff, 0x09, 0x02, 0x82, 0xff, 0x02, 0xff, 0x02, 0x03, 0xff, + 0x08, 0xff, 0xff, 0x05, 0xff, 0x00, 0x03, 0xff, 0xff, 0x02, 0x03, 0xff, 0x03, 0xff, 0x83, 0x03, + 0x04, 0xff, 0xff, 0x05, 0x84, 0x04, 0x04, 0xff, 0xff, 0x02, 0x0f, 0xff, 0x04, 0xff, 0xff, 0x07, + 0xff, 0x05, 0x05, 0x85, 0x04, 0xff, 0xff, 0x05, 0x06, 0xff, 0xff, 0x05, 0xff, 0x0e, 0x03, 0xff, + 0xff, 0x0c, 0x01, 0xff, 0x0a, 0xff, 0xff, 0x09, 0x0a, 0xff, 0xff, 0x0b, 0x8a, 0x0a, 0x0a, 0xff, + 0x08, 0xff, 0xff, 0x0b, 0xff, 0x00, 0x0d, 0xff, 0xff, 0x0b, 0x0b, 0x8b, 0x0a, 0xff, 0xff, 0x0b, + 0x0c, 0x8c, 0xff, 0x0c, 0xff, 0x0c, 0x0d, 0xff, 0xff, 0x0c, 0x0f, 0xff, 0x0a, 0xff, 0xff, 0x07, + 0xff, 0x0c, 0x0d, 0xff, 0x0d, 0xff, 0x8d, 0x0d, 0x06, 0xff, 0xff, 0x0b, 0xff, 0x0e, 0x0d, 0xff, + 0x08, 0xff, 0xff, 0x09, 0xff, 0x09, 0x09, 0x89, 0xff, 0x02, 0x0f, 0xff, 0x0a, 0xff, 0xff, 0x09, + 0x88, 0x08, 0x08, 0xff, 0x08, 0xff, 0xff, 0x09, 0x08, 0xff, 0xff, 0x0b, 0xff, 0x0e, 0x03, 0xff, + 0xff, 0x0c, 0x0f, 0xff, 0x04, 0xff, 0xff, 0x09, 0x0f, 0xff, 0x8f, 0x0f, 0xff, 0x0e, 0x0f, 0xff, + 0x08, 0xff, 0xff, 0x05, 0xff, 0x0e, 0x0d, 0xff, 0xff, 0x0e, 0x0f, 0xff, 0x0e, 0x8e, 0xff, 0x0e, + }; + + public static int Decode2418(byte[] data, int offset) + { + return ((data[offset + 2] & 0x7F) << 11) | + ((data[offset + 1] & 0x7F) << 4) | + ((data[offset] & 0x70) >> 3) | + ((data[offset] & 0x4) >> 2); + } + + public static byte Decode84(byte[] data, int offset) + { + return Decode(data[offset]); + } + + public static byte Decode(byte b1, byte b2) + { + return (byte)((_unhammingTable[b2] << 4) | (_unhammingTable[b1] & 0xF)); + } + + public static byte Decode(byte b) + { + return (byte)(_unhammingTable[b] & 0xF); + } + + } +} \ No newline at end of file diff --git a/libse/TransportStream/ObjectDataSegment.cs b/libse/TransportStream/ObjectDataSegment.cs new file mode 100644 index 000000000..ae49e0963 --- /dev/null +++ b/libse/TransportStream/ObjectDataSegment.cs @@ -0,0 +1,468 @@ +using System.Collections.Generic; +using System.Drawing; + +namespace Nikse.SubtitleEdit.Core.TransportStream +{ + public class ObjectDataSegment + { + public int ObjectId { get; set; } + public int ObjectVersionNumber { get; set; } + + /// + /// 0x00 coding of pixels, 0x01 coded as a string of characters + /// + public int ObjectCodingMethod { get; set; } + + public bool NonModifyingColorFlag { get; set; } + + public int TopFieldDataBlockLength { get; set; } + public int BottomFieldDataBlockLength { get; set; } + + public int NumberOfCodes { get; set; } + + public List FirstDataTypes = new List(); + public Bitmap Image { get; set; } + private FastBitmap _fastImage; + + public const int PixelDecoding2Bit = 0x10; + public const int PixelDecoding4Bit = 0x11; + public const int PixelDecoding8Bit = 0x12; + public const int MapTable2To4Bit = 0x20; + public const int MapTable2To8Bit = 0x21; + public const int MapTable4To8Bit = 0x22; + public const int EndOfObjectLineCode = 0xf0; + + public int BufferIndex { get; private set; } + + public ObjectDataSegment(byte[] buffer, int index) + { + ObjectId = Helper.GetEndianWord(buffer, index); + ObjectVersionNumber = buffer[index + 2] >> 4; + ObjectCodingMethod = (buffer[index + 2] & Helper.B00001100) >> 2; + NonModifyingColorFlag = (buffer[index + 2] & Helper.B00000010) > 0; + TopFieldDataBlockLength = Helper.GetEndianWord(buffer, index + 3); + BottomFieldDataBlockLength = Helper.GetEndianWord(buffer, index + 5); + BufferIndex = index; + } + + public void DecodeImage(byte[] buffer, int index, ClutDefinitionSegment cds) + { + if (ObjectCodingMethod == 0) + { + var twoToFourBitColorLookup = new List { 0, 1, 2, 3 }; + var twoToEightBitColorLookup = new List { 0, 1, 2, 3 }; + var fourToEightBitColorLookup = new List { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; + + int pixelCode = 0; + int runLength = 0; + int dataType = buffer[index + 7]; + int length = TopFieldDataBlockLength; + + index += 8; + int start = index; + int x = 0; + int y = 0; + + // Pre-decoding to determine image size + int width = 0; + while (index < start + TopFieldDataBlockLength) + { + index = CalculateSize(buffer, index, ref dataType, start, ref x, ref y, length, ref runLength, ref width); + } + if (width > 2000) + width = 2000; + if (y > 500) + y = 500; + Image = new Bitmap(width, y + 1); + _fastImage = new FastBitmap(Image); + _fastImage.LockImage(); + + x = 0; + y = 0; + index = start; + while (index < start + TopFieldDataBlockLength) + { + index = ProcessDataType(buffer, index, cds, ref dataType, start, twoToFourBitColorLookup, fourToEightBitColorLookup, twoToEightBitColorLookup, ref x, ref y, length, ref pixelCode, ref runLength); + } + + x = 0; + y = 1; + if (BottomFieldDataBlockLength == 0) + { + index = start; + } + else + { + length = BottomFieldDataBlockLength; + index = start + TopFieldDataBlockLength; + start = index; + } + dataType = buffer[index - 1]; + while (index < start + BottomFieldDataBlockLength - 1) + { + index = ProcessDataType(buffer, index, cds, ref dataType, start, twoToFourBitColorLookup, fourToEightBitColorLookup, twoToEightBitColorLookup, ref x, ref y, length, ref pixelCode, ref runLength); + } + _fastImage.UnlockImage(); + } + else if (ObjectCodingMethod == 1) + { + Image = new Bitmap(1, 1); + NumberOfCodes = buffer[index + 3]; + } + } + + private int ProcessDataType(byte[] buffer, int index, ClutDefinitionSegment cds, ref int dataType, int start, + List twoToFourBitColorLookup, List fourToEightBitColorLookup, List twoToEightBitColorLookup, + ref int x, ref int y, int length, ref int pixelCode, ref int runLength) + { + if (dataType == PixelDecoding2Bit) + { + int bitIndex = 0; + while (index < start + length - 1 && TwoBitPixelDecoding(buffer, ref index, ref bitIndex, out pixelCode, out runLength)) + { + DrawPixels(cds, twoToFourBitColorLookup[pixelCode], runLength, ref x, ref y); + } + } + else if (dataType == PixelDecoding4Bit) + { + bool startHalf = false; + while (index < start + length - 1 && FourBitPixelDecoding(buffer, ref index, ref startHalf, out pixelCode, out runLength)) + { + DrawPixels(cds, fourToEightBitColorLookup[pixelCode], runLength, ref x, ref y); + } + } + else if (dataType == PixelDecoding8Bit) + { + while (index < start + length - 1 && EightBitPixelDecoding(buffer, ref index, out pixelCode, out runLength)) + { + DrawPixels(cds, pixelCode, runLength, ref x, ref y); + } + } + else if (dataType == MapTable2To4Bit) + { + //4 entry numbers of 4-bits each; entry number 0 first, entry number 3 last + twoToFourBitColorLookup[0] = buffer[index] >> 4; + twoToFourBitColorLookup[1] = buffer[index] & Helper.B00001111; + index++; + twoToFourBitColorLookup[2] = buffer[index] >> 4; + twoToFourBitColorLookup[3] = buffer[index] & Helper.B00001111; + index++; + } + else if (dataType == MapTable2To8Bit) + { + //4 entry numbers of 8-bits each; entry number 0 first, entry number 3 last + twoToEightBitColorLookup[0] = buffer[index]; + index++; + twoToEightBitColorLookup[1] = buffer[index]; + index++; + twoToEightBitColorLookup[2] = buffer[index]; + index++; + twoToEightBitColorLookup[3] = buffer[index]; + index++; + } + else if (dataType == MapTable4To8Bit) + { + // 16 entry numbers of 8-bits each + for (int k = 0; k < 16; k++) + { + fourToEightBitColorLookup[k] = buffer[index]; + index++; + } + } + else if (dataType == EndOfObjectLineCode) + { + x = 0; + y += 2; // interlaced - skip one line + } + + if (index < start + length) + { + dataType = buffer[index]; + index++; + } + + return index; + } + + private static int CalculateSize(byte[] buffer, int index, ref int dataType, int start, ref int x, ref int y, int length, ref int runLength, ref int width) + { + int pixelCode; + if (dataType == PixelDecoding2Bit) + { + int bitIndex = 0; + while (index < start + length - 1 && TwoBitPixelDecoding(buffer, ref index, ref bitIndex, out pixelCode, out runLength)) + { + x += runLength; + } + } + else if (dataType == PixelDecoding4Bit) + { + bool startHalf = false; + while (index < start + length - 1 && FourBitPixelDecoding(buffer, ref index, ref startHalf, out pixelCode, out runLength)) + { + x += runLength; + } + } + else if (dataType == PixelDecoding8Bit) + { + while (index < start + length - 1 && EightBitPixelDecoding(buffer, ref index, out pixelCode, out runLength)) + { + x += runLength; + } + } + else if (dataType == MapTable2To4Bit) + { + index += 2; + } + else if (dataType == MapTable2To8Bit) + { + index += 4; + } + else if (dataType == MapTable4To8Bit) + { + index += 16; + } + else if (dataType == EndOfObjectLineCode) + { + x = 0; + y += 2; // interlaced - skip one line + } + + if (index < start + length) + { + dataType = buffer[index]; + index++; + } + if (x > width) + width = x; + return index; + } + + private void DrawPixels(ClutDefinitionSegment cds, int pixelCode, int runLength, ref int x, ref int y) + { + var c = Color.Red; + if (cds != null) + { + foreach (var item in cds.Entries) + { + if (item.ClutEntryId == pixelCode) + { + c = item.GetColor(); + break; + } + } + } + + for (int k = 0; k < runLength; k++) + { + if (y < _fastImage.Height && x < _fastImage.Width) + _fastImage.SetPixel(x, y, c); + x++; + } + } + + private static int Next8Bits(byte[] buffer, ref int index) + { + int result = buffer[index]; + index++; + return result; + } + + private static bool EightBitPixelDecoding(byte[] buffer, ref int index, out int pixelCode, out int runLength) + { + pixelCode = 0; + runLength = 1; + int firstByte = Next8Bits(buffer, ref index); + if (firstByte != 0) + { + runLength = 1; + pixelCode = firstByte; + } + else + { + int nextByte = Next8Bits(buffer, ref index); + if (nextByte >> 7 == 0) + { + if (nextByte != 0) + runLength = nextByte & Helper.B01111111; // 1-127 + else + return false; + } + else + { + runLength = nextByte & Helper.B01111111; // 3-127 + pixelCode = Next8Bits(buffer, ref index); + } + } + return true; + } + + private static int Next4Bits(byte[] buffer, ref int index, ref bool startHalf) + { + int result; + if (startHalf) + { + startHalf = false; + result = buffer[index] & Helper.B00001111; + index++; + } + else + { + startHalf = true; + result = buffer[index] >> 4; + } + return result; + } + + private static bool FourBitPixelDecoding(byte[] buffer, ref int index, ref bool startHalf, out int pixelCode, out int runLength) + { + pixelCode = 0; + runLength = 1; + int first = Next4Bits(buffer, ref index, ref startHalf); + if (first != 0) + { + pixelCode = first; // Next4Bits(buffer, ref index, ref startHalf); + runLength = 1; + } + else + { + int next1 = Next4Bits(buffer, ref index, ref startHalf); + if ((next1 & Helper.B00001000) == 0) + { + if (next1 != 0) + { + runLength = (next1 & Helper.B00000111) + 2; // 3-9 + } + else + { + if (startHalf) + { + startHalf = false; + index++; + } + return false; + } + } + else if (next1 == Helper.B00001100) + { + runLength = 1; + pixelCode = 0; + } + else if (next1 == Helper.B00001101) + { + runLength = 2; + pixelCode = 0; + } + else + { + int next2 = Next4Bits(buffer, ref index, ref startHalf); + if ((next1 & Helper.B00000100) == 0) + { + runLength = (next1 & Helper.B00000011) + 4; // 4-7 + pixelCode = next2; + } + else + { + int next3 = Next4Bits(buffer, ref index, ref startHalf); + if ((next1 & Helper.B00000001) == 0) + { + runLength = next2 + 9; // 9-24 + pixelCode = next3; + } + else if (next1 == Helper.B00001111) + { + runLength = ((next2 << 4) + next3) + 25; // 25-280 + pixelCode = Next4Bits(buffer, ref index, ref startHalf); + } + } + } + } + return true; + } + + private static int Next2Bits(byte[] buffer, ref int index, ref int bitIndex) + { + int result; + if (bitIndex == 0) + { + bitIndex++; + result = (buffer[index] & Helper.B11000000) >> 6; + } + else if (bitIndex == 1) + { + bitIndex++; + result = (buffer[index] & Helper.B00110000) >> 4; + } + else if (bitIndex == 2) + { + bitIndex++; + result = (buffer[index] & Helper.B00001100) >> 2; + } + else // 3 - last bit pair + { + bitIndex = 0; + result = buffer[index] & Helper.B00000011; + index++; + } + return result; + } + + private static bool TwoBitPixelDecoding(byte[] buffer, ref int index, ref int bitIndex, out int pixelCode, out int runLength) + { + runLength = 1; + pixelCode = 0; + int first = Next2Bits(buffer, ref index, ref bitIndex); + if (first != 0) + { + runLength = 1; + pixelCode = first; + } + else + { + int next = Next2Bits(buffer, ref index, ref bitIndex); + if (next == 1) + { + runLength = 1; + pixelCode = 0; + } + else if (next > 1) + { + runLength = ((next & Helper.B00000001) << 2) + Next2Bits(buffer, ref index, ref bitIndex) + 3; // 3-10 + pixelCode = Next2Bits(buffer, ref index, ref bitIndex); + } + else + { + int next2 = Next2Bits(buffer, ref index, ref bitIndex); + if (next2 == Helper.B00000001) + { + runLength = 2; + pixelCode = 0; + } + else if (next2 == Helper.B00000010) + { + runLength = (Next2Bits(buffer, ref index, ref bitIndex) << 2) + // 12-27 + Next2Bits(buffer, ref index, ref bitIndex) + 12; + pixelCode = Next2Bits(buffer, ref index, ref bitIndex); + } + else if (next2 == Helper.B00000011) + { + runLength = (Next2Bits(buffer, ref index, ref bitIndex) << 6) + // 29 - 284 + (Next2Bits(buffer, ref index, ref bitIndex) << 4) + + (Next2Bits(buffer, ref index, ref bitIndex) << 2) + + Next2Bits(buffer, ref index, ref bitIndex) + 29; + + pixelCode = Next2Bits(buffer, ref index, ref bitIndex); + } + else + { + if (bitIndex != 0) + index++; + return false; // end of 2-bit/pixel code string + } + } + } + return true; + } + + } +} diff --git a/libse/TransportStream/Packet.cs b/libse/TransportStream/Packet.cs new file mode 100644 index 000000000..c22be1c4a --- /dev/null +++ b/libse/TransportStream/Packet.cs @@ -0,0 +1,143 @@ +using System; + +namespace Nikse.SubtitleEdit.Core.TransportStream +{ + /// + /// MPEG transport stream packet + /// + public class Packet + { + /// + /// ID byte of TS Packet + /// + public const byte SynchronizationByte = 0x47; // 74 decimal, or 01000111 binary + + /// + /// Null packets can ensure that the stream maintains a constant bitrate. Null packets is to be ignored + /// + public const int NullPacketId = 0x1FFF; + + /// + /// Program Association Table: lists all programs available in the transport stream. + /// + public const int ProgramAssociationTablePacketId = 0; + + /// + /// + /// + public const int ConditionalAccessTablePacketId = 1; + + /// + /// + /// + public const int TransportStreamDescriptionTablePacketId = 2; + + /// + /// Set by demodulator if can't correct errors in the stream, to tell the demultiplexer that the packet has an uncorrectable error + /// + public bool TransportErrorIndicator { get; set; } + + /// + /// Start of PES data or PSI + /// + public bool PayloadUnitStartIndicator { get; set; } + + /// + /// Higher priority than other packets with the same PID + /// + public bool TransportPriority { get; set; } + + /// + /// Program Identifier + /// + public int PacketId { get; set; } + + /// + /// 1 = Reserved for future use, 10 = Scrambled with even key, 11 = Scrambled with odd key + /// + public int ScramblingControl { get; set; } + + /// + /// 1 = no adaptation fields (payload only), 10 = adaptation field only, 11 = adaptation field and payload + /// + public int AdaptationFieldControl { get; set; } + + /// + /// Incremented only when a payload is present (AdaptationFieldExist = 10 or 11). Starts at 0. + /// + public int ContinuityCounter { get; set; } + + public int AdaptionFieldLength { get; set; } + + public AdaptationField AdaptationField { get; private set; } + + public bool IsNullPacket { get { return PacketId == NullPacketId; } } + + public bool IsProgramAssociationTable { get { return PacketId == ProgramAssociationTablePacketId; } } + + public byte[] Payload { get; private set; } + + public bool IsPrivateStream1 + { + get + { + if (Payload == null || Payload.Length < 4) + return false; + + return Payload[0] == 0 && + Payload[1] == 0 && + Payload[2] == 1 && + Payload[3] == 0xbd; // 0xbd == 189 - MPEG-2 Private stream 1 (non MPEG audio, subpictures) + } + } + + public bool IsVideoStream + { + get + { + if (Payload == null || Payload.Length < 4) + return false; + + return Payload[0] == 0 && + Payload[1] == 0 && + Payload[2] == 1 && + Payload[3] >= 0xE0 && + Payload[3] < 0xF0; + } + } + + public ProgramAssociationTable ProgramAssociationTable { get; private set; } + + public Packet(byte[] packetBuffer) + { + TransportErrorIndicator = 1 == packetBuffer[1] >> 7; // Set by demodulator if can't correct errors in the stream, to tell the demultiplexer that the packet has an uncorrectable error + PayloadUnitStartIndicator = 1 == ((packetBuffer[1] & 64) >> 6); // and with 01000000 to get second byte - 1 means start of PES data or PSI otherwise zero + TransportPriority = 1 == ((packetBuffer[1] & 32) >> 5); // and with 00100000 to get third byte - 1 means higher priority than other packets with the same PID + PacketId = (packetBuffer[1] & 31) * 256 + packetBuffer[2];// and with 00011111 to get last 5 bytes + ScramblingControl = packetBuffer[3] >> 6; // '00' = Not scrambled. The following per DVB spec:[12] '01' = Reserved for future use, '10' = Scrambled with even key, '11' = Scrambled with odd key + AdaptationFieldControl = (packetBuffer[3] & 48) >> 4; // and with 00110000, 01 = no adaptation fields (payload only), 10 = adaptation field only, 11 = adaptation field and payload + ContinuityCounter = packetBuffer[3] & 15; + AdaptionFieldLength = AdaptationFieldControl > 1 ? (0xFF & packetBuffer[4]) + 1 : 0; + + if (AdaptationFieldControl == Helper.B00000010 || AdaptationFieldControl == Helper.B00000011) + AdaptationField = new AdaptationField(packetBuffer); + + if (AdaptationFieldControl == Helper.B00000001 || AdaptationFieldControl == Helper.B00000011) // Payload exists - binary '01' || '11' + { + int payloadStart = 4; + if (AdaptationField != null) + payloadStart += (1 + AdaptationField.Length); + + if (PacketId == ProgramAssociationTablePacketId) // PAT = Program Association Table: lists all programs available in the transport stream. + { + ProgramAssociationTable = new ProgramAssociationTable(packetBuffer, payloadStart + 1); // TODO: What index? + } + + // Save payload + Payload = new byte[packetBuffer.Length - payloadStart]; + Buffer.BlockCopy(packetBuffer, payloadStart, Payload, 0, Payload.Length); + } + } + + } +} \ No newline at end of file diff --git a/libse/TransportStream/PacketizedElementaryStream.cs b/libse/TransportStream/PacketizedElementaryStream.cs new file mode 100644 index 000000000..ce748d781 --- /dev/null +++ b/libse/TransportStream/PacketizedElementaryStream.cs @@ -0,0 +1,96 @@ +using System; + +namespace Nikse.SubtitleEdit.Core.TransportStream +{ + /// + /// http://www.mpucoder.com/DVD/pes-hdr.html + /// + public class PacketizedElementaryStream + { + public const int HeaderLength = 6; + public const int Mpeg2HeaderLength = 14; + + public readonly UInt32 StartCode; + public readonly int StreamId; + public readonly int Length; + public readonly int ScramblingControl; + public readonly int Priority; + public readonly int DataAlignmentIndicator; + public readonly int Copyright; + public readonly int OriginalOrCopy; + public readonly int PresentationTimestampDecodeTimestampFlags; + public readonly int ElementaryStreamClockReferenceFlag; + public readonly int EsRateFlag; + public readonly int DsmTrickModeFlag; + public readonly int AdditionalCopyInfoFlag; + public readonly int CrcFlag; + public readonly int ExtensionFlag; + public readonly int HeaderDataLength; + + public readonly UInt64? PresentationTimestamp; + public readonly UInt64? DecodeTimestamp; + + public readonly int? SubPictureStreamId; + + public readonly byte[] DataBuffer; + + public PacketizedElementaryStream(byte[] buffer, int index) + { + StartCode = Helper.GetEndian(buffer, index + 0, 3); + StreamId = buffer[index + 3]; + Length = Helper.GetEndianWord(buffer, index + 4); + + ScramblingControl = (buffer[index + 6] >> 4) & Helper.B00000011; + Priority = buffer[index + 6] & Helper.B00001000; + DataAlignmentIndicator = buffer[index + 6] & Helper.B00000100; + Copyright = buffer[index + 6] & Helper.B00000010; + OriginalOrCopy = buffer[index + 6] & Helper.B00000001; + PresentationTimestampDecodeTimestampFlags = buffer[index + 7] >> 6; + ElementaryStreamClockReferenceFlag = buffer[index + 7] & Helper.B00100000; + EsRateFlag = buffer[index + 7] & Helper.B00010000; + DsmTrickModeFlag = buffer[index + 7] & Helper.B00001000; + AdditionalCopyInfoFlag = buffer[index + 7] & Helper.B00000100; + CrcFlag = buffer[index + 7] & Helper.B00001000; + ExtensionFlag = buffer[index + 7] & Helper.B00000010; + + HeaderDataLength = buffer[index + 8]; + + if (StreamId == 0xBD) // 10111101 binary = 189 decimal = 0xBD hex -> private_stream_1 + { + int id = buffer[index + 9 + HeaderDataLength]; + if (id >= 0x20 && id <= 0x40) // x3f 0r x40 ? + SubPictureStreamId = id; + } + + int tempIndex = index + 9; + if (PresentationTimestampDecodeTimestampFlags == Helper.B00000010 || + PresentationTimestampDecodeTimestampFlags == Helper.B00000011) + { + string bString = Helper.GetBinaryString(buffer, tempIndex, 5); + bString = bString.Substring(4, 3) + bString.Substring(8, 15) + bString.Substring(24, 15); + PresentationTimestamp = Convert.ToUInt64(bString, 2); + tempIndex += 5; + } + if (PresentationTimestampDecodeTimestampFlags == Helper.B00000011) + { + string bString = Helper.GetBinaryString(buffer, tempIndex, 5); + bString = bString.Substring(4, 3) + bString.Substring(8, 15) + bString.Substring(24, 15); + DecodeTimestamp = Convert.ToUInt64(bString, 2); + } + + int dataIndex = index + HeaderDataLength + 24 - Mpeg2HeaderLength; + int dataSize = Length - (4 + HeaderDataLength); + + if (dataSize < 0 || (dataSize + dataIndex > buffer.Length)) // to fix bad subs... + { + dataSize = buffer.Length - dataIndex; + if (dataSize < 0) + return; + } + + DataBuffer = new byte[dataSize+1]; + //Buffer.BlockCopy(buffer, dataIndex, DataBuffer, 0, dataSize); + Buffer.BlockCopy(buffer, dataIndex - 1, DataBuffer, 0, DataBuffer.Length); // why subtract one from dataIndex??? + } + } +} diff --git a/libse/TransportStream/PageCompositionSegemnt.cs b/libse/TransportStream/PageCompositionSegemnt.cs new file mode 100644 index 000000000..519a275c8 --- /dev/null +++ b/libse/TransportStream/PageCompositionSegemnt.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; + +namespace Nikse.SubtitleEdit.Core.TransportStream +{ + public class PageCompositionSegment + { + public int PageTimeOut { get; set; } + public int PageVersionNumber { get; set; } + public int PageState { get; set; } + public List Regions = new List(); + + public PageCompositionSegment(byte[] buffer, int index, int regionLength) + { + PageTimeOut = buffer[index]; + PageVersionNumber = buffer[index + 1] >> 4; + PageState = (buffer[index + 1] & Helper.B00001100) >> 2; + Regions = new List(); + int i = 0; + while (i < regionLength && i + index < buffer.Length) + { + var rcsr = new PageCompositionSegmentRegion(); + rcsr.RegionId = buffer[i + index + 2]; + i += 2; + rcsr.RegionHorizontalAddress = Helper.GetEndianWord(buffer, i + index + 2); + i += 2; + rcsr.RegionVerticalAddress = Helper.GetEndianWord(buffer, i + index + 2); + i += 2; + Regions.Add(rcsr); + } + } + } + +} \ No newline at end of file diff --git a/libse/TransportStream/PageCompositionSegmentRegion.cs b/libse/TransportStream/PageCompositionSegmentRegion.cs new file mode 100644 index 000000000..fb9a053e9 --- /dev/null +++ b/libse/TransportStream/PageCompositionSegmentRegion.cs @@ -0,0 +1,10 @@ +namespace Nikse.SubtitleEdit.Core.TransportStream +{ + public class PageCompositionSegmentRegion + { + public int RegionId { get; set; } + public int RegionHorizontalAddress { get; set; } + public int RegionVerticalAddress { get; set; } + } + +} \ No newline at end of file diff --git a/libse/TransportStream/ProgramAssociationTable.cs b/libse/TransportStream/ProgramAssociationTable.cs new file mode 100644 index 000000000..ede2fe000 --- /dev/null +++ b/libse/TransportStream/ProgramAssociationTable.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; + +namespace Nikse.SubtitleEdit.Core.TransportStream +{ + public class ProgramAssociationTable + { + public int TableId { get; set; } + public int SectionLength { get; set; } + public int TransportStreamId { get; set; } + public int VersionNumber { get; set; } + public int CurrentNextIndicator { get; set; } + public int SectionNumber { get; set; } + public int LastSectionNumber { get; set; } + public List ProgramNumbers { get; set; } + public List ProgramIds { get; set; } + + public ProgramAssociationTable(byte[] packetBuffer, int index) + { + TableId = packetBuffer[index]; + SectionLength = (packetBuffer[index + 1] & Helper.B00000011) * 256 + packetBuffer[index + 2]; + TransportStreamId = packetBuffer[index + 3] * 256 + packetBuffer[index + 4]; + VersionNumber = (packetBuffer[index + 5] & Helper.B00111110) >> 1; + CurrentNextIndicator = packetBuffer[index + 5] & 1; + SectionNumber = packetBuffer[index + 6]; + LastSectionNumber = packetBuffer[index + 7]; + + ProgramNumbers = new List(); + ProgramIds = new List(); + index = index + 8; + for (int i = 0; i < (SectionLength - 5) / 8; i++) + { + if (index + 3 < packetBuffer.Length) + { + int programNumber = packetBuffer[index] * 256 + packetBuffer[index + 1]; + int programId = (packetBuffer[index + 2] & Helper.B00011111) * 256 + packetBuffer[index + 3]; + ProgramNumbers.Add(programNumber); + ProgramIds.Add(programId); + index += 8; + } + } + } + } +} \ No newline at end of file diff --git a/libse/TransportStream/RegionClutSegmentEntry.cs b/libse/TransportStream/RegionClutSegmentEntry.cs new file mode 100644 index 000000000..cfab97e25 --- /dev/null +++ b/libse/TransportStream/RegionClutSegmentEntry.cs @@ -0,0 +1,59 @@ +using System.Drawing; + +namespace Nikse.SubtitleEdit.Core.TransportStream +{ + public class RegionClutSegmentEntry + { + public int ClutEntryId { get; set; } + public bool ClutEntry2BitClutEntryFlag { get; set; } + public bool ClutEntry4BitClutEntryFlag { get; set; } + public bool ClutEntry8BitClutEntryFlag { get; set; } + public bool FullRangeFlag { get; set; } + public int ClutEntryY { get; set; } + public int ClutEntryCr { get; set; } + public int ClutEntryCb { get; set; } + public int ClutEntryT { get; set; } + + private static int BoundByteRange(int i) + { + if (i < byte.MinValue) + return byte.MinValue; + if (i > byte.MaxValue) + return byte.MaxValue; + return i; + } + + public Color GetColor() + { + double y, cr, cb; + if (FullRangeFlag) + { + y = ClutEntryY; + cr = ClutEntryCr; + cb = ClutEntryCb; + } + else + { + y = ClutEntryY * 255 / 63.0; + cr = ClutEntryCr * 255 / 15.0; + cb = ClutEntryCb * 255 / 15.0; + } + + // Calculate rgb - based on Project X + int r = (int)(y + (1.402f * (cr - 128))); + int g = (int)(y - (0.34414 * (cb - 128)) - (0.71414 * (cr - 128))); + int b = (int)(y + (1.722 * (cb - 128))); + + int t = byte.MaxValue - BoundByteRange(ClutEntryT); + r = BoundByteRange(r); + g = BoundByteRange(g); + b = BoundByteRange(b); + + if (y < 0.1) // full transparency + t = 0; + + return Color.FromArgb(t, r, g, b); + } + } + +} \ No newline at end of file diff --git a/libse/TransportStream/RegionCompositionSegment.cs b/libse/TransportStream/RegionCompositionSegment.cs new file mode 100644 index 000000000..6d6d24c6c --- /dev/null +++ b/libse/TransportStream/RegionCompositionSegment.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; + +namespace Nikse.SubtitleEdit.Core.TransportStream +{ + public class RegionCompositionSegment + { + public int RegionId { get; set; } + public int RegionVersionNumber { get; set; } + public bool RegionFillFlag { get; set; } + public int RegionWidth { get; set; } + public int RegionHeight { get; set; } + public int RegionLevelOfCompatibility { get; set; } + public int RegionDepth { get; set; } + public int RegionClutId { get; set; } + public int Region8BitPixelCode { get; set; } + public int Region4BitPixelCode { get; set; } + public int Region2BitPixelCode { get; set; } + + public List Objects = new List(); + + public RegionCompositionSegment(byte[] buffer, int index, int regionLength) + { + RegionId = buffer[index]; + RegionVersionNumber = buffer[index + 1] >> 4; + RegionFillFlag = (buffer[index + 1] & Helper.B00001000) > 0; + RegionWidth = Helper.GetEndianWord(buffer, index + 2); + RegionHeight = Helper.GetEndianWord(buffer, index + 4); + RegionLevelOfCompatibility = buffer[index + 6] >> 5; + RegionDepth = (buffer[index + 6] & Helper.B00011100) >> 2; + RegionClutId = buffer[index + 7]; + Region8BitPixelCode = buffer[index + 8]; + Region4BitPixelCode = buffer[index + 9] >> 4; + Region2BitPixelCode = (buffer[index + 9] & Helper.B00001100) >> 2; + int i = 0; + while (i < regionLength && i + index < buffer.Length) + { + var rcso = new RegionCompositionSegmentObject(); + rcso.ObjectId = Helper.GetEndianWord(buffer, i + index + 10); + i += 2; + rcso.ObjectType = buffer[i + index + 10] >> 6; + rcso.ObjectProviderFlag = (buffer[index + i + 10] & Helper.B00110000) >> 4; + rcso.ObjectHorizontalPosition = ((buffer[index + i + 10] & Helper.B00001111) << 8) + buffer[index + i + 11]; + i += 2; + rcso.ObjectVerticalPosition = ((buffer[index + i + 10] & Helper.B00001111) << 8) + buffer[index + i + 11]; + i += 2; + if (rcso.ObjectType == 1 || rcso.ObjectType == 2) + i += 2; + Objects.Add(rcso); + } + } + } +} \ No newline at end of file diff --git a/libse/TransportStream/RegionCompositionSegmentObject.cs b/libse/TransportStream/RegionCompositionSegmentObject.cs new file mode 100644 index 000000000..6165153ef --- /dev/null +++ b/libse/TransportStream/RegionCompositionSegmentObject.cs @@ -0,0 +1,13 @@ +namespace Nikse.SubtitleEdit.Core.TransportStream +{ + public class RegionCompositionSegmentObject + { + public int ObjectId { get; set; } + public int ObjectType { get; set; } + public int ObjectProviderFlag { get; set; } + public int ObjectHorizontalPosition { get; set; } + public int ObjectVerticalPosition { get; set; } + public int? ObjectForegroundPixelCode { get; set; } + public int? ObjectBackgroundPixelCode { get; set; } + } +} \ No newline at end of file diff --git a/libse/TransportStream/SubtitleSegment.cs b/libse/TransportStream/SubtitleSegment.cs new file mode 100644 index 000000000..bdd3ae20c --- /dev/null +++ b/libse/TransportStream/SubtitleSegment.cs @@ -0,0 +1,83 @@ +namespace Nikse.SubtitleEdit.Core.TransportStream +{ + + public class SubtitleSegment + { + public const int PageCompositionSegment = 0x10; + public const int RegionCompositionSegment = 0x11; + public const int ClutDefinitionSegment = 0x12; + public const int ObjectDataSegment = 0x13; + public const int DisplayDefinitionSegment = 0x14; + public const int EndOfDisplaySetSegment = 0x80; + + public int SyncByte { get; set; } + public int SegmentType { get; set; } + public int PageId { get; set; } + public int SegmentLength { get; set; } + public bool IsValid { get; set; } + + public ClutDefinitionSegment ClutDefinition; + public ObjectDataSegment ObjectData; + public DisplayDefinitionSegment DisplayDefinition; + public PageCompositionSegment PageComposition; + public RegionCompositionSegment RegionComposition; + + public SubtitleSegment(byte[] buffer, int index) + { + if (buffer == null || buffer.Length < 7) + return; + + SyncByte = buffer[index]; + SegmentType = buffer[index + 1]; + PageId = Helper.GetEndianWord(buffer, index + 2); + SegmentLength = Helper.GetEndianWord(buffer, index + 4); + + if (buffer.Length - 6 < SegmentLength) + return; + + if (index + 6 + SegmentLength > buffer.Length) + return; + + IsValid = true; + + switch (SegmentType) + { + case PageCompositionSegment: + PageComposition = new PageCompositionSegment(buffer, index + 6, SegmentLength - 2); + break; + case RegionCompositionSegment: + RegionComposition = new RegionCompositionSegment(buffer, index + 6, SegmentLength - 10); + break; + case ClutDefinitionSegment: + ClutDefinition = new ClutDefinitionSegment(buffer, index + 6, SegmentLength); + break; + case ObjectDataSegment: + ObjectData = new ObjectDataSegment(buffer, index + 6); + break; + case DisplayDefinitionSegment: + DisplayDefinition = new DisplayDefinitionSegment(buffer, index + 6); + break; + case EndOfDisplaySetSegment: + break; + } + } + + public string SegmentTypeDescription + { + get + { + switch (SegmentType) + { + case PageCompositionSegment: return "Page composition segment"; + case RegionCompositionSegment: return "Region composition segment"; + case ClutDefinitionSegment: return "CLUT definition segment"; + case ObjectDataSegment: return "Object data segment"; + case DisplayDefinitionSegment: return "Display definition segment"; + case EndOfDisplaySetSegment: return "End of display set segment"; + default: return "Unknown"; + } + } + } + } + +} diff --git a/libse/TransportStream/TransportStreamParser.cs b/libse/TransportStream/TransportStreamParser.cs new file mode 100644 index 000000000..d88169632 --- /dev/null +++ b/libse/TransportStream/TransportStreamParser.cs @@ -0,0 +1,443 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Nikse.SubtitleEdit.Core.BluRaySup; +using Nikse.SubtitleEdit.Core.VobSub; + +namespace Nikse.SubtitleEdit.Core.TransportStream +{ + /// + /// MPEG transport stream parser + /// + public class TransportStreamParser + { + public delegate void LoadTransportStreamCallback(long position, long total); + + public int NumberOfNullPackets { get; private set; } + public long TotalNumberOfPackets { get; private set; } + public long TotalNumberOfPrivateStream1 { get; private set; } + public long TotalNumberOfPrivateStream1Continuation0 { get; private set; } + public List SubtitlePacketIds { get; private set; } + public List SubtitlePackets { get; private set; } + // public List ProgramAssociationTables { get; private set; } + private Dictionary> SubtitlesLookup { get; set; } + private Dictionary> DvbSubtitlesLookup { get; set; } + public bool IsM2TransportStream { get; private set; } + public ulong FirstVideoPts { get; private set; } + + public void Parse(string fileName, LoadTransportStreamCallback callback) + { + using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + Parse(fs, callback); + } + } + + /// + /// Can be used with e.g. MemoryStream or FileStream + /// + /// Input stream + public void Parse(Stream ms, LoadTransportStreamCallback callback) + { + bool firstVideoPtsFound = false; + IsM2TransportStream = false; + NumberOfNullPackets = 0; + TotalNumberOfPackets = 0; + TotalNumberOfPrivateStream1 = 0; + TotalNumberOfPrivateStream1Continuation0 = 0; + SubtitlePacketIds = new List(); + SubtitlePackets = new List(); + // ProgramAssociationTables = new List(); + ms.Position = 0; + const int packetLength = 188; + DetectFormat(ms); + var packetBuffer = new byte[packetLength]; + var m2TsTimeCodeBuffer = new byte[4]; + long position = 0; + + // check for Topfield .rec file + ms.Seek(position, SeekOrigin.Begin); + ms.Read(m2TsTimeCodeBuffer, 0, 3); + if (m2TsTimeCodeBuffer[0] == 0x54 && m2TsTimeCodeBuffer[1] == 0x46 && m2TsTimeCodeBuffer[2] == 0x72) + position = 3760; + + long transportStreamLength = ms.Length; + while (position < transportStreamLength) + { + ms.Seek(position, SeekOrigin.Begin); + + if (IsM2TransportStream) + { + ms.Read(m2TsTimeCodeBuffer, 0, m2TsTimeCodeBuffer.Length); + //var tc = (m2tsTimeCodeBuffer[0]<< 24) | (m2tsTimeCodeBuffer[1] << 16) | (m2tsTimeCodeBuffer[2] << 8) | (m2tsTimeCodeBuffer[3]); + position += m2TsTimeCodeBuffer.Length; + } + + ms.Read(packetBuffer, 0, packetLength); + byte syncByte = packetBuffer[0]; + if (syncByte == Packet.SynchronizationByte) + { + var packet = new Packet(packetBuffer); + + if (packet.IsNullPacket) + { + NumberOfNullPackets++; + } + else if (!firstVideoPtsFound && packet.IsVideoStream) + { + if (packet.Payload != null && packet.Payload.Length > 10) + { + int presentationTimestampDecodeTimestampFlags = packet.Payload[7] >> 6; + if (presentationTimestampDecodeTimestampFlags == Helper.B00000010 || + presentationTimestampDecodeTimestampFlags == Helper.B00000011) + { + FirstVideoPts = (ulong)packet.Payload[9 + 4] >> 1; + FirstVideoPts += (ulong)packet.Payload[9 + 3] << 7; + FirstVideoPts += (ulong)(packet.Payload[9 + 2] & Helper.B11111110) << 14; + FirstVideoPts += (ulong)packet.Payload[9 + 1] << 22; + FirstVideoPts += (ulong)(packet.Payload[9 + 0] & Helper.B00001110) << 29; + firstVideoPtsFound = true; + } + } + } + else if (packet.IsProgramAssociationTable) + { + //var sb = new StringBuilder(); + //sb.AppendLine("PacketNo: " + TotalNumberOfPackets + 1); + //sb.AppendLine("PacketId: " + packet.PacketId); + //sb.AppendLine(); + //sb.AppendLine("TransportErrorIndicator: " + packet.TransportErrorIndicator); + //sb.AppendLine("PayloadUnitStartIndicator: " + packet.PayloadUnitStartIndicator); + //sb.AppendLine("TransportPriority: " + packet.TransportPriority); + //sb.AppendLine("ScramblingControl: " + packet.ScramblingControl); + //sb.AppendLine("AdaptationFieldExist: " + packet.AdaptationFieldControl); + //sb.AppendLine("ContinuityCounter: " + packet.ContinuityCounter); + //sb.AppendLine(); + //if (packet.AdaptationField != null) + //{ + //sb.AppendLine("AdaptationFieldLength: " + packet.AdaptationField.Length); + //sb.AppendLine("DiscontinuityIndicator: " + packet.AdaptationField.DiscontinuityIndicator); + //sb.AppendLine("RandomAccessIndicator: " + packet.AdaptationField.RandomAccessIndicator); + //sb.AppendLine("ElementaryStreamPriorityIndicator: " + packet.AdaptationField.ElementaryStreamPriorityIndicator); + //sb.AppendLine("PcrFlag: " + packet.AdaptationField.PcrFlag); + //sb.AppendLine("OpcrFlag: " + packet.AdaptationField.OpcrFlag); + //sb.AppendLine("SplicingPointFlag: " + packet.AdaptationField.SplicingPointFlag); + //sb.AppendLine("TransportPrivateDataFlag: " + packet.AdaptationField.TransportPrivateDataFlag); + //sb.AppendLine("AdaptationFieldExtensionFlag: " + packet.AdaptationField.AdaptationFieldExtensionFlag); + //sb.AppendLine(); + //} + //sb.AppendLine("TableId: " + packet.ProgramAssociationTable.TableId); + //sb.AppendLine("SectionLength: " + packet.ProgramAssociationTable.SectionLength); + //sb.AppendLine("TransportStreamId: " + packet.ProgramAssociationTable.TransportStreamId); + //sb.AppendLine("VersionNumber: " + packet.ProgramAssociationTable.VersionNumber); + //sb.AppendLine("CurrentNextIndicator: " + packet.ProgramAssociationTable.CurrentNextIndicator); + //sb.AppendLine("SectionNumber: " + packet.ProgramAssociationTable.SectionNumber); + //sb.AppendLine("LastSectionNumber: " + packet.ProgramAssociationTable.LastSectionNumber); + //ProgramAssociationTables.Add(packet); + } + else if (packet.IsPrivateStream1 || SubtitlePacketIds.Contains(packet.PacketId)) + { + TotalNumberOfPrivateStream1++; + + SubtitlePackets.Add(packet); + + if (!SubtitlePacketIds.Contains(packet.PacketId)) + { + SubtitlePacketIds.Add(packet.PacketId); + } + if (packet.ContinuityCounter == 0) + { + TotalNumberOfPrivateStream1Continuation0++; + + //int pesExtensionlength = 0; + //if (12 + packet.AdaptionFieldLength < packetBuffer.Length) + // pesExtensionlength = 0xFF & packetBuffer[12 + packet.AdaptionFieldLength]; + //int pesOffset = 13 + packet.AdaptionFieldLength + pesExtensionlength; + //bool isTeletext = (pesExtensionlength == 0x24 && (0xFF & packetBuffer[pesOffset]) >> 4 == 1); + + //// workaround uk freesat teletext + //if (!isTeletext) + // isTeletext = (pesExtensionlength == 0x24 && (0xFF & packetBuffer[pesOffset]) == 0x99); + + //if (!isTeletext) + //{ + + //} + } + if (callback != null) + { + callback.Invoke(ms.Position, transportStreamLength); + } + } + TotalNumberOfPackets++; + position += packetLength; + } + else + { + position++; + } + } + + if (IsM2TransportStream) + { + DvbSubtitlesLookup = new Dictionary>(); + SubtitlesLookup = new Dictionary>(); + foreach (int pid in SubtitlePacketIds) + { + var bdMs = new MemoryStream(); + var list = MakeSubtitlePesPackets(pid, SubtitlePackets); + var startMsList = new List(); + var endMsList = new List(); + foreach (var item in list) + { + item.WriteToStream(bdMs); + if (item.DataIdentifier == 0x16) + { + if (startMsList.Count <= endMsList.Count) + startMsList.Add(item.PresentationTimestampToMilliseconds()); + else + endMsList.Add(item.PresentationTimestampToMilliseconds()); + } + //else if (item.DataBuffer[0] == 0x80) + //{ // TODO: Load bd sub after 0x80, so we can be sure to get correct time code??? + // endMsList.Add(item.PresentationTimestampToMilliseconds() / 90); + //} + } + SubtitlesLookup.Add(pid, list); + + bdMs.Position = 0; + var sb = new StringBuilder(); + var bdList = BluRaySupParser.ParseBluRaySup(bdMs, sb, true); + if (bdList.Count > 0) + { + var subList = new List(); + for (int k = 0; k < bdList.Count; k++) + { + var bdSup = bdList[k]; + ulong startMs = 0; + if (k < startMsList.Count) + startMs = startMsList[k]; + ulong endMs = 0; + if (k < endMsList.Count) + endMs = endMsList[k]; + subList.Add(new TransportStreamSubtitle(bdSup, startMs, endMs, (ulong)((FirstVideoPts + 45) / 90.0))); + } + DvbSubtitlesLookup.Add(pid, subList); + } + } + SubtitlePacketIds.Clear(); + foreach (int key in DvbSubtitlesLookup.Keys) + SubtitlePacketIds.Add(key); + SubtitlePacketIds.Sort(); + return; + } + + // check for SubPictureStreamId = 32 + SubtitlesLookup = new Dictionary>(); + foreach (int pid in SubtitlePacketIds) + { + var list = MakeSubtitlePesPackets(pid, SubtitlePackets); + bool hasImageSubtitles = false; + foreach (var item in list) + { + if (item.IsDvbSubpicture) + { + hasImageSubtitles = true; + break; + } + } + if (hasImageSubtitles) + { + SubtitlesLookup.Add(pid, list); + } + } + SubtitlePacketIds.Clear(); + foreach (int key in SubtitlesLookup.Keys) + SubtitlePacketIds.Add(key); + SubtitlePacketIds.Sort(); + + // Merge packets and set start/end time + DvbSubtitlesLookup = new Dictionary>(); + var firstVideoMs = (ulong)((FirstVideoPts + 45) / 90.0); + foreach (int pid in SubtitlePacketIds) + { + var subtitles = new List(); + var list = GetSubtitlePesPackets(pid); + if (list != null) + { + for (int i = 0; i < list.Count; i++) + { + var pes = list[i]; + pes.ParseSegments(); + if (pes.ObjectDataList.Count > 0) + { + var sub = new TransportStreamSubtitle(); + sub.StartMilliseconds = pes.PresentationTimestampToMilliseconds(); + sub.Pes = pes; + if (i + 1 < list.Count && list[i + 1].PresentationTimestampToMilliseconds() > 25) + sub.EndMilliseconds = list[i + 1].PresentationTimestampToMilliseconds() - 25; + if (sub.EndMilliseconds < sub.StartMilliseconds) + sub.EndMilliseconds = sub.StartMilliseconds + 3500; + subtitles.Add(sub); + if (sub.StartMilliseconds < firstVideoMs) + firstVideoMs = sub.StartMilliseconds; + } + } + } + foreach (var s in subtitles) + { + s.OffsetMilliseconds = firstVideoMs; + } + DvbSubtitlesLookup.Add(pid, subtitles); + } + SubtitlePacketIds.Clear(); + foreach (int key in DvbSubtitlesLookup.Keys) + { + if (DvbSubtitlesLookup[key].Count > 0) + SubtitlePacketIds.Add(key); + } + SubtitlePacketIds.Sort(); + } + + public List GetDvbSubtitles(int packetId) + { + if (DvbSubtitlesLookup.ContainsKey(packetId)) + return DvbSubtitlesLookup[packetId]; + return null; + } + + internal static List MakeSubtitlePesPackets(int packetId, List subtitlePackets) + { + var list = new List(); + int last = -1; + var packetList = new List(); + foreach (Packet packet in subtitlePackets) + { + if (packet.PacketId == packetId) + { + if (packet.PayloadUnitStartIndicator) + { + if (packetList.Count > 0) + AddPesPacket(list, packetList); + packetList = new List(); + } + if (packet.Payload != null && last != packet.ContinuityCounter) + packetList.Add(packet); + last = packet.ContinuityCounter; + } + } + if (packetList.Count > 0) + AddPesPacket(list, packetList); + return list; + } + + public List GetSubtitlePesPackets(int packetId) + { + if (SubtitlesLookup.ContainsKey(packetId)) + return SubtitlesLookup[packetId]; + return null; + } + + private static void AddPesPacket(List list, List packetList) + { + int bufferSize = 0; + foreach (Packet p in packetList) + bufferSize += p.Payload.Length; + var pesData = new byte[bufferSize]; + int pesIndex = 0; + foreach (Packet p in packetList) + { + Buffer.BlockCopy(p.Payload, 0, pesData, pesIndex, p.Payload.Length); + pesIndex += p.Payload.Length; + } + + DvbSubPes pes; + if (VobSubParser.IsMpeg2PackHeader(pesData)) + { + pes = new DvbSubPes(pesData, Mpeg2Header.Length); + } + else if (VobSubParser.IsPrivateStream1(pesData, 0)) + { + pes = new DvbSubPes(pesData, 0); + } + else + { + pes = new DvbSubPes(pesData, 0); + } + list.Add(pes); + } + + internal void DetectFormat(Stream ms) + { + if (ms.Length > 192 + 192 + 5) + { + ms.Seek(0, SeekOrigin.Begin); + var buffer = new byte[192 + 192 + 5]; + ms.Read(buffer, 0, buffer.Length); + if (buffer[0] == Packet.SynchronizationByte && buffer[188] == Packet.SynchronizationByte) + return; + if (buffer[4] == Packet.SynchronizationByte && buffer[192 + 4] == Packet.SynchronizationByte && buffer[192 + 192 + 4] == Packet.SynchronizationByte) + { + IsM2TransportStream = true; + } + } + } + + public static bool IsDvbSup(string fileName) + { + try + { + byte[] pesData = File.ReadAllBytes(fileName); + if (pesData[0] != 0x20 || pesData[1] != 0 || pesData[2] != 0x0F) + return false; + + var pes = new DvbSubPes(0, pesData); + return pes.SubtitleSegments.Count > 0; + } + catch + { + return false; + } + } + + public static List GetDvbSup(string fileName) + { + byte[] pesData = File.ReadAllBytes(fileName); + var list = new List(); + int index = 0; + while (index < pesData.Length - 10) + { + var pes = new DvbSubPes(index, pesData); + index = pes.Length + 1; + list.Add(pes); + } + + var subtitles = new List(); + int seconds = 0; + for (int i = 0; i < list.Count; i++) + { + var pes = list[i]; + pes.ParseSegments(); + if (pes.ObjectDataList.Count > 0) + { + var sub = new TransportStreamSubtitle(); + sub.StartMilliseconds = (ulong)seconds * 1000UL; + seconds += pes.PageCompositions[0].PageTimeOut; + if (pes.PageCompositions.Count > 0) + sub.EndMilliseconds = sub.StartMilliseconds + (ulong)pes.PageCompositions[0].PageTimeOut * 1000UL; + else + sub.EndMilliseconds = sub.StartMilliseconds + 2500; + sub.Pes = pes; + subtitles.Add(sub); + } + if (pes.PageCompositions.Count > 0) + { + seconds += pes.PageCompositions[0].PageTimeOut; + } + } + return subtitles; + } + + } +} \ No newline at end of file diff --git a/libse/TransportStream/TransportStreamSubtitle.cs b/libse/TransportStream/TransportStreamSubtitle.cs new file mode 100644 index 000000000..4bfb388b1 --- /dev/null +++ b/libse/TransportStream/TransportStreamSubtitle.cs @@ -0,0 +1,103 @@ +using System.Drawing; + +namespace Nikse.SubtitleEdit.Core.TransportStream +{ + public class TransportStreamSubtitle + { + private ulong _startMilliseconds; + public ulong StartMilliseconds + { + get + { + if (_startMilliseconds < OffsetMilliseconds) + return 0; + return _startMilliseconds - OffsetMilliseconds; + } + set + { + _startMilliseconds = value + OffsetMilliseconds; + } + } + + private ulong _endMilliseconds; + public ulong EndMilliseconds + { + get + { + if (_endMilliseconds < OffsetMilliseconds) + return 0; + return _endMilliseconds - OffsetMilliseconds; + } + set + { + _endMilliseconds = value + OffsetMilliseconds; + } + } + + public ulong OffsetMilliseconds { get; set; } + public DvbSubPes Pes { get; set; } + private BluRaySup.BluRaySupParser.PcsData _bdSup; + public int? ActiveImageIndex { get; set; } + + public bool IsBluRaySup + { + get + { + return _bdSup != null; + } + } + + public bool IsDvbSub + { + get + { + return Pes != null; + } + } + + public TransportStreamSubtitle(BluRaySup.BluRaySupParser.PcsData bdSup, ulong startMilliseconds, ulong endMilliseconds) + { + _bdSup = bdSup; + StartMilliseconds = startMilliseconds; + EndMilliseconds = endMilliseconds; + } + + public TransportStreamSubtitle(BluRaySup.BluRaySupParser.PcsData bdSup, ulong startMilliseconds, ulong endMilliseconds, ulong offset) + { + _bdSup = bdSup; + StartMilliseconds = startMilliseconds; + EndMilliseconds = endMilliseconds; + OffsetMilliseconds = offset; + } + + public TransportStreamSubtitle() + { + } + + /// + /// Gets full image if 'ActiveImageIndex' not set, otherwise only gets image by index + /// + /// + public Bitmap GetActiveImage() + { + if (_bdSup != null) + return _bdSup.GetBitmap(); + + if (ActiveImageIndex.HasValue && ActiveImageIndex >= 0 && ActiveImageIndex < Pes.ObjectDataList.Count) + return (Bitmap)Pes.GetImage(Pes.ObjectDataList[ActiveImageIndex.Value]).Clone(); + return Pes.GetImageFull(); + } + + public int NumberOfImages + { + get + { + if (Pes != null) + return Pes.ObjectDataList.Count; + else + return _bdSup.BitmapObjects.Count; + } + } + + } +} \ No newline at end of file diff --git a/libse/UknownFormatImporter.cs b/libse/UknownFormatImporter.cs new file mode 100644 index 000000000..c0e33a238 --- /dev/null +++ b/libse/UknownFormatImporter.cs @@ -0,0 +1,691 @@ +using System; +using System.Text; +using System.Text.RegularExpressions; +using Nikse.SubtitleEdit.Core.SubtitleFormats; + +namespace Nikse.SubtitleEdit.Core +{ + public class UknownFormatImporter + { + + public bool UseFrames { get; set; } + + public Subtitle AutoGuessImport(string[] lines) + { + var subtitle = ImportTimeCodesOnSameSeperateLine(lines); + if (subtitle.Paragraphs.Count < 2) + subtitle = ImportTimeCodesAndTextOnSameLineOnlySpaceAsSeparator(lines); + + var subTcAndTextOnSameLine = ImportTimeCodesAndTextOnSameLine(lines); + if (subTcAndTextOnSameLine.Paragraphs.Count > subtitle.Paragraphs.Count) + subtitle = subTcAndTextOnSameLine; + + var subTcOnAloneLines = ImportTimeCodesOnAloneLines(lines); + if (subTcOnAloneLines.Paragraphs.Count > subtitle.Paragraphs.Count) + subtitle = subTcOnAloneLines; + + if (subtitle.Paragraphs.Count < 2) + { + subtitle = ImportTimeCodesInFramesOnSameSeperateLine(lines); + if (subtitle.Paragraphs.Count < 2) + { + subtitle = ImportTimeCodesInFramesAndTextOnSameLine(lines); + } + } + + if (subtitle.Paragraphs.Count > 1) + CleanUp(subtitle); + + return subtitle; + } + + private static void CleanUp(Subtitle subtitle) + { + foreach (Paragraph p in subtitle.Paragraphs) + { + p.Text = p.Text.Replace("
", string.Empty); + p.Text = p.Text.Replace("
", string.Empty); + p.Text = p.Text.Replace("", string.Empty); + p.Text = p.Text.Replace("", string.Empty); + p.Text = p.Text.Replace("
", string.Empty); + p.Text = p.Text.Replace(" ", " "); + p.Text = p.Text.Replace(" ", " "); + p.Text = p.Text.Replace(" ", " "); + p.Text = p.Text.Replace("|", Environment.NewLine).Replace("

", Environment.NewLine).Replace("

", Environment.NewLine).Trim(); + p.Text = p.Text.Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine).Trim(); + p.Text = p.Text.Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine).Trim(); + } + subtitle.RemoveEmptyLines(); + } + + private Subtitle ImportTimeCodesInFramesAndTextOnSameLine(string[] lines) + { + var regexTimeCodes1 = new Regex(@"\d+", RegexOptions.Compiled); + Paragraph p = null; + var subtitle = new Subtitle(); + var sb = new StringBuilder(); + for (int idx = 0; idx < lines.Length; idx++) + { + string line = lines[idx]; + + var matches = regexTimeCodes1.Matches(line); + if (matches.Count >= 2) + { + string start = matches[0].ToString(); + string end = matches[1].ToString(); + + if (p != null) + { + p.Text = sb.ToString().Trim(); + subtitle.Paragraphs.Add(p); + } + p = new Paragraph(); + sb = new StringBuilder(); + try + { + if (UseFrames) + { + p.StartFrame = int.Parse(start); + p.EndFrame = int.Parse(end); + p.CalculateTimeCodesFromFrameNumbers(Configuration.Settings.General.CurrentFrameRate); + } + else + { + p.StartTime.TotalMilliseconds = double.Parse(start); + p.EndTime.TotalMilliseconds = double.Parse(end); + } + } + catch + { + p = null; + } + + if (matches[0].Index < 9) + line = line.Remove(0, matches[0].Index); + line = line.Replace(matches[0].ToString(), string.Empty); + line = line.Replace(matches[1].ToString(), string.Empty); + line = line.Trim(); + if (line.StartsWith("}{}") || line.StartsWith("][]")) + line = line.Remove(0, 3); + line = line.Trim(); + } + if (p != null && line.Length > 1) + { + sb.AppendLine(line.Trim()); + if (sb.Length > 200) + return new Subtitle(); + } + } + if (p != null) + { + p.Text = sb.ToString().Trim(); + subtitle.Paragraphs.Add(p); + } + subtitle.Renumber(); + return subtitle; + } + + private Subtitle ImportTimeCodesInFramesOnSameSeperateLine(string[] lines) + { + Paragraph p = null; + var subtitle = new Subtitle(); + var sb = new StringBuilder(); + for (int idx = 0; idx < lines.Length; idx++) + { + string line = lines[idx]; + string lineWithPerhapsOnlyNumbers = line.Replace(" ", string.Empty).Replace(".", string.Empty).Replace(",", string.Empty).Replace("\t", string.Empty).Replace(":", string.Empty).Replace(";", string.Empty).Replace("{", string.Empty).Replace("}", string.Empty).Replace("[", string.Empty).Replace("]", string.Empty).Replace("-", string.Empty).Replace(">", string.Empty).Replace("<", string.Empty); + bool allNumbers = lineWithPerhapsOnlyNumbers.Length > 0; + foreach (char c in lineWithPerhapsOnlyNumbers) + { + if (!char.IsDigit(c)) + allNumbers = false; + } + if (allNumbers && lineWithPerhapsOnlyNumbers.Length > 2) + { + string[] arr = line.Replace("-", " ").Replace(">", " ").Replace("{", " ").Replace("}", " ").Replace("[", " ").Replace("]", " ").Trim().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + if (arr.Length == 2) + { + string[] start = arr[0].Trim().Split(new[] { '.', ',', ';', ':' }, StringSplitOptions.RemoveEmptyEntries); + string[] end = arr[0].Trim().Split(new[] { '.', ',', ';', ':' }, StringSplitOptions.RemoveEmptyEntries); + if (start.Length == 1 && end.Length == 1) + { + if (p != null) + { + p.Text = sb.ToString().Trim(); + subtitle.Paragraphs.Add(p); + } + p = new Paragraph(); + sb = new StringBuilder(); + try + { + if (UseFrames) + { + p.StartFrame = int.Parse(start[0]); + p.EndFrame = int.Parse(end[0]); + p.CalculateTimeCodesFromFrameNumbers(Configuration.Settings.General.CurrentFrameRate); + } + else + { + p.StartTime.TotalMilliseconds = double.Parse(start[0]); + p.EndTime.TotalMilliseconds = double.Parse(end[0]); + } + } + catch + { + p = null; + } + } + } + else if (arr.Length == 3) + { + string[] start = arr[0].Trim().Split(new[] { '.', ',', ';', ':' }, StringSplitOptions.RemoveEmptyEntries); + string[] end = arr[0].Trim().Split(new[] { '.', ',', ';', ':' }, StringSplitOptions.RemoveEmptyEntries); + string[] duration = arr[0].Trim().Split(new[] { '.', ',', ';', ':' }, StringSplitOptions.RemoveEmptyEntries); + + if (end.Length == 1 && duration.Length == 1) + { + start = end; + end = duration; + } + + if (start.Length == 1 && end.Length == 1) + { + if (p != null) + { + p.Text = sb.ToString().Trim(); + subtitle.Paragraphs.Add(p); + } + p = new Paragraph(); + sb = new StringBuilder(); + try + { + if (UseFrames) + { + p.StartFrame = int.Parse(start[0]); + p.EndFrame = int.Parse(end[0]); + p.CalculateTimeCodesFromFrameNumbers(Configuration.Settings.General.CurrentFrameRate); + } + else + { + p.StartTime.TotalMilliseconds = double.Parse(start[0]); + p.EndTime.TotalMilliseconds = double.Parse(end[0]); + } + } + catch + { + p = null; + } + } + } + } + if (p != null && !allNumbers && line.Length > 1) + { + line = line.Trim(); + if (line.StartsWith("}{}") || line.StartsWith("][]")) + line = line.Remove(0, 3); + sb.AppendLine(line.Trim()); + } + } + if (p != null) + { + p.Text = sb.ToString().Trim(); + subtitle.Paragraphs.Add(p); + } + subtitle.CalculateTimeCodesFromFrameNumbers(Configuration.Settings.General.CurrentFrameRate); + subtitle.Renumber(); + return subtitle; + } + + private static Subtitle ImportTimeCodesOnAloneLines(string[] lines) + { + Paragraph p = null; + var subtitle = new Subtitle(); + var sb = new StringBuilder(); + for (int idx = 0; idx < lines.Length; idx++) + { + string line = lines[idx]; + string lineWithPerhapsOnlyNumbers = line.Replace(" ", string.Empty).Replace(".", string.Empty).Replace(",", string.Empty).Replace("\t", string.Empty).Replace(":", string.Empty).Replace(";", string.Empty).Replace("{", string.Empty).Replace("}", string.Empty).Replace("[", string.Empty).Replace("]", string.Empty).Replace("-", string.Empty).Replace(">", string.Empty).Replace("<", string.Empty); + bool allNumbers = lineWithPerhapsOnlyNumbers.Length > 0; + foreach (char c in lineWithPerhapsOnlyNumbers) + { + if (!char.IsDigit(c)) + allNumbers = false; + } + if (allNumbers && lineWithPerhapsOnlyNumbers.Length > 5) + { + string[] arr = line.Replace("-", " ").Replace(">", " ").Replace("{", " ").Replace("}", " ").Replace("[", " ").Replace("]", " ").Trim().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + if (arr.Length == 1) + { + string[] tc = arr[0].Trim().Split(new[] { '.', ',', ';', ':' }, StringSplitOptions.RemoveEmptyEntries); + if (p == null || Math.Abs(p.EndTime.TotalMilliseconds) > 0.001) + { + if (p != null) + { + p.Text = sb.ToString().Trim(); + subtitle.Paragraphs.Add(p); + sb = new StringBuilder(); + } + p = new Paragraph { StartTime = DecodeTime(tc) }; + } + else + { + p.EndTime = DecodeTime(tc); + } + } + } + if (p != null && !allNumbers && line.Length > 1) + { + line = line.Trim(); + if (line.StartsWith("}{}") || line.StartsWith("][]")) + line = line.Remove(0, 3); + sb.AppendLine(line.Trim()); + } + } + if (p != null) + { + p.Text = sb.ToString().Trim(); + subtitle.Paragraphs.Add(p); + } + subtitle.Renumber(); + return subtitle; + } + + private static Subtitle ImportTimeCodesAndTextOnSameLine(string[] lines) + { + var regexTimeCodes1 = new Regex(@"\d+[:.,;]{1}\d\d[:.,;]{1}\d\d[:.,;]{1}\d+", RegexOptions.Compiled); + var regexTimeCodes2 = new Regex(@"\d+[:.,;]{1}\d\d[:.,;]{1}\d+", RegexOptions.Compiled); + Paragraph p = null; + var subtitle = new Subtitle(); + var sb = new StringBuilder(); + + bool isFirstLineNumber = false; + + int count = -1; + for (int idx = 0; idx < lines.Length; idx++) + { + string line = lines[idx]; + var matches = regexTimeCodes1.Matches(line); + if (matches.Count == 0) + matches = regexTimeCodes2.Matches(line); + if (matches.Count == 2) + { + var start = matches[0].Value.Split(new[] { '.', ',', ';', ':' }, StringSplitOptions.RemoveEmptyEntries); + int i; + if (int.TryParse(start[0], out i)) + { + if (count == -1 && i < 2) + count = i; + if (count != i) + { + isFirstLineNumber = false; + break; + } + count++; + } + } + if (count > 2) + isFirstLineNumber = true; + } + + for (int idx = 0; idx < lines.Length; idx++) + { + string line = lines[idx]; + + if (isFirstLineNumber) + { + while (line.Length > 0 && char.IsDigit(line[0])) + { + line = line.Remove(0, 1); + } + } + + var matches = regexTimeCodes1.Matches(line); + if (matches.Count == 0) + matches = regexTimeCodes2.Matches(line); + if (matches.Count == 2) + { + string[] start = matches[0].ToString().Split(new[] { '.', ',', ';', ':' }, StringSplitOptions.RemoveEmptyEntries); + string[] end = matches[1].ToString().Split(new[] { '.', ',', ';', ':' }, StringSplitOptions.RemoveEmptyEntries); + if ((start.Length == 3 || start.Length == 4) && (end.Length == 3 || end.Length == 4)) + { + if (p != null) + { + p.Text = sb.ToString().Trim(); + subtitle.Paragraphs.Add(p); + } + p = new Paragraph(); + sb = new StringBuilder(); + p.StartTime = DecodeTime(start); + p.EndTime = DecodeTime(end); + } + if (matches[0].Index < 9) + line = line.Remove(0, matches[0].Index); + + line = line.Replace(matches[0].ToString(), string.Empty); + line = line.Replace(matches[1].ToString(), string.Empty); + line = line.Trim(); + if (line.StartsWith("}{}") || line.StartsWith("][]")) + line = line.Remove(0, 3); + line = line.Trim(); + } + if (p != null && line.Length > 1) + sb.AppendLine(line.Trim()); + } + if (p != null) + { + p.Text = sb.ToString().Trim(); + subtitle.Paragraphs.Add(p); + } + + // remove all equal headers + if (subtitle.Paragraphs.Count > 5) + { + string prefix = subtitle.Paragraphs[0].Text; + foreach (Paragraph paragraph in subtitle.Paragraphs) + { + string text = paragraph.Text.Trim(); + var newPrefix = new StringBuilder(); + int i = 0; + while (i < prefix.Length && i < text.Length && text[i] == prefix[i]) + { + newPrefix.Append(text[i]); + i++; + } + prefix = newPrefix.ToString(); + } + if (prefix.Length > 3 && prefix[1] == ':' && prefix[2] == '\\') + prefix = string.Empty; + + if (prefix.Length > 0) + { + foreach (Paragraph paragraph in subtitle.Paragraphs) + { + string text = paragraph.Text.Trim(); + if (text.StartsWith(prefix)) + paragraph.Text = text.Remove(0, prefix.Length); + } + } + } + + subtitle.Renumber(); + return subtitle; + } + + private static Subtitle ImportTimeCodesAndTextOnSameLineOnlySpaceAsSeparator(string[] lines) + { + var regexTimeCodes1 = new Regex(@"\d+ {1}\d\d {1}\d\d {1}\d+", RegexOptions.Compiled); + var regexTimeCodes2 = new Regex(@"\d+ {1}\d\d {1}\d+", RegexOptions.Compiled); + Paragraph p = null; + var subtitle = new Subtitle(); + var sb = new StringBuilder(); + for (int idx = 0; idx < lines.Length; idx++) + { + string line = lines[idx]; + + var matches = regexTimeCodes1.Matches(line); + if (matches.Count == 0) + matches = regexTimeCodes2.Matches(line); + if (matches.Count == 2) + { + string[] start = matches[0].ToString().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + string[] end = matches[1].ToString().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + if ((start.Length == 3 || start.Length == 4) && (end.Length == 3 || end.Length == 4)) + { + if (p != null) + { + p.Text = sb.ToString().Trim(); + subtitle.Paragraphs.Add(p); + } + p = new Paragraph(); + sb = new StringBuilder(); + p.StartTime = DecodeTime(start); + p.EndTime = DecodeTime(end); + } + if (matches[0].Index < 9) + line = line.Remove(0, matches[0].Index); + line = line.Replace(matches[0].ToString(), string.Empty); + line = line.Replace(matches[1].ToString(), string.Empty); + line = line.Trim(); + if (line.StartsWith("}{}") || line.StartsWith("][]")) + line = line.Remove(0, 3); + line = line.Trim(); + } + if (p != null && line.Length > 1) + sb.AppendLine(line.Trim()); + } + if (p != null) + { + p.Text = sb.ToString().Trim(); + subtitle.Paragraphs.Add(p); + } + subtitle.Renumber(); + return subtitle; + } + + private static Subtitle ImportTimeCodesOnSameSeperateLine(string[] lines) + { + Paragraph p = null; + var subtitle = new Subtitle(); + var sb = new StringBuilder(); + for (int idx = 0; idx < lines.Length; idx++) + { + string line = lines[idx]; + string lineWithPerhapsOnlyNumbers = line.Replace(" ", string.Empty).Replace(".", string.Empty).Replace(",", string.Empty).Replace("\t", string.Empty).Replace(":", string.Empty).Replace(";", string.Empty).Replace("{", string.Empty).Replace("}", string.Empty).Replace("[", string.Empty).Replace("]", string.Empty).Replace("-", string.Empty).Replace(">", string.Empty).Replace("<", string.Empty); + bool allNumbers = lineWithPerhapsOnlyNumbers.Length > 0; + foreach (char c in lineWithPerhapsOnlyNumbers) + { + if (!char.IsDigit(c)) + allNumbers = false; + } + if (allNumbers && lineWithPerhapsOnlyNumbers.Length > 5) + { + string[] arr = line.Replace("-", " ").Replace(">", " ").Replace("{", " ").Replace("}", " ").Replace("[", " ").Replace("]", " ").Trim().Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); + if (arr.Length == 2) + { + string[] start = arr[0].Trim().Split(new[] { '.', ',', ';', ':' }, StringSplitOptions.RemoveEmptyEntries); + string[] end = arr[1].Trim().Split(new[] { '.', ',', ';', ':' }, StringSplitOptions.RemoveEmptyEntries); + if ((start.Length == 3 || start.Length == 4) && (end.Length == 3 || end.Length == 4)) + { + if (p != null) + { + p.Text = sb.ToString().Trim(); + subtitle.Paragraphs.Add(p); + } + p = new Paragraph(); + sb = new StringBuilder(); + p.StartTime = DecodeTime(start); + p.EndTime = DecodeTime(end); + } + } + else if (arr.Length == 3) + { + string[] start = arr[0].Trim().Split(new[] { '.', ',', ';', ':' }, StringSplitOptions.RemoveEmptyEntries); + string[] end = arr[1].Trim().Split(new[] { '.', ',', ';', ':' }, StringSplitOptions.RemoveEmptyEntries); + string[] duration = arr[2].Trim().Split(new[] { '.', ',', ';', ':' }, StringSplitOptions.RemoveEmptyEntries); + + if (start.Length < 3) + { + start = end; + end = duration; + } + + if ((start.Length == 3 || start.Length == 4) && (end.Length == 3 || end.Length == 4)) + { + if (p != null) + { + p.Text = sb.ToString().Trim(); + subtitle.Paragraphs.Add(p); + } + p = new Paragraph(); + sb = new StringBuilder(); + p.StartTime = DecodeTime(start); + p.EndTime = DecodeTime(end); + } + } + } + if (p != null && !allNumbers && line.Length > 1) + { + line = line.Trim(); + if (line.StartsWith("}{}") || line.StartsWith("][]")) + line = line.Remove(0, 3); + sb.AppendLine(line.Trim()); + } + } + if (p != null) + { + p.Text = sb.ToString().Trim(); + subtitle.Paragraphs.Add(p); + } + + double averateDuration = 0; + foreach (Paragraph a in subtitle.Paragraphs) + { + double d = a.Duration.TotalSeconds; + if (d > 10) + d = 8; + averateDuration += d; + } + averateDuration = averateDuration / subtitle.Paragraphs.Count; + if (averateDuration < 0.2 || (averateDuration < 0.5 && subtitle.Paragraphs.Count > 100 && subtitle.Paragraphs[subtitle.Paragraphs.Count - 1].StartTime.TotalSeconds < 140 && subtitle.Paragraphs[subtitle.Paragraphs.Count - 2].StartTime.TotalSeconds < 140)) + { + subtitle = ImportTimeCodesOnSameSeperateLineNoMilliseconds(lines); + int i = 0; + foreach (Paragraph a in subtitle.Paragraphs) + { + i++; + var next = subtitle.GetParagraphOrDefault(i); + if (next != null && a.EndTime.TotalMilliseconds >= next.StartTime.TotalMilliseconds) + { + a.EndTime.TotalMilliseconds = next.StartTime.TotalMilliseconds - Configuration.Settings.General.MinimumMillisecondsBetweenLines; + } + } + return subtitle; + } + + subtitle.Renumber(); + return subtitle; + } + + private static Subtitle ImportTimeCodesOnSameSeperateLineNoMilliseconds(string[] lines) + { + Paragraph p = null; + var subtitle = new Subtitle(); + var sb = new StringBuilder(); + for (int idx = 0; idx < lines.Length; idx++) + { + string line = lines[idx]; + string lineWithPerhapsOnlyNumbers = line.Replace(" ", string.Empty).Replace(".", string.Empty).Replace(",", string.Empty).Replace("\t", string.Empty).Replace(":", string.Empty).Replace(";", string.Empty).Replace("{", string.Empty).Replace("}", string.Empty).Replace("[", string.Empty).Replace("]", string.Empty).Replace("-", string.Empty).Replace(">", string.Empty).Replace("<", string.Empty); + bool allNumbers = lineWithPerhapsOnlyNumbers.Length > 0; + foreach (char c in lineWithPerhapsOnlyNumbers) + { + if (!char.IsDigit(c)) + allNumbers = false; + } + if (allNumbers && lineWithPerhapsOnlyNumbers.Length > 5) + { + string[] arr = line.Replace("-", " ").Replace(">", " ").Replace("{", " ").Replace("}", " ").Replace("[", " ").Replace("]", " ").Trim().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + if (arr.Length == 2) + { + string[] start = arr[0].Trim().Split(new[] { '.', ',', ';', ':' }, StringSplitOptions.RemoveEmptyEntries); + string[] end = arr[1].Trim().Split(new[] { '.', ',', ';', ':' }, StringSplitOptions.RemoveEmptyEntries); + if ((start.Length == 3 || start.Length == 4) && (end.Length == 3 || end.Length == 4)) + { + if (start.Length == 3) + start = (arr[0].Trim() + ".000").Split(new[] { '.', ',', ';', ':' }, StringSplitOptions.RemoveEmptyEntries); + if (end.Length == 3) + end = (arr[1].Trim() + ".000").Split(new[] { '.', ',', ';', ':' }, StringSplitOptions.RemoveEmptyEntries); + + if (p != null) + { + p.Text = sb.ToString().Trim(); + subtitle.Paragraphs.Add(p); + } + p = new Paragraph(); + sb = new StringBuilder(); + p.StartTime = DecodeTime(start); + p.EndTime = DecodeTime(end); + } + } + else if (arr.Length == 3) + { + string[] start = arr[0].Trim().Split(new[] { '.', ',', ';', ':' }, StringSplitOptions.RemoveEmptyEntries); + string[] end = arr[1].Trim().Split(new[] { '.', ',', ';', ':' }, StringSplitOptions.RemoveEmptyEntries); + string[] duration = arr[2].Trim().Split(new[] { '.', ',', ';', ':' }, StringSplitOptions.RemoveEmptyEntries); + + if (start.Length == 3) + start = (arr[0].Trim() + ".000").Split(new[] { '.', ',', ';', ':' }, StringSplitOptions.RemoveEmptyEntries); + if (end.Length == 3) + end = (arr[1].Trim() + ".000").Split(new[] { '.', ',', ';', ':' }, StringSplitOptions.RemoveEmptyEntries); + if (duration.Length == 3) + duration = (arr[2].Trim() + ".000").Split(new[] { '.', ',', ';', ':' }, StringSplitOptions.RemoveEmptyEntries); + + if (start.Length < 3) + { + start = end; + end = duration; + } + + if ((start.Length == 3 || start.Length == 4) && (end.Length == 3 || end.Length == 4)) + { + if (p != null) + { + p.Text = sb.ToString().Trim(); + subtitle.Paragraphs.Add(p); + } + p = new Paragraph(); + sb = new StringBuilder(); + p.StartTime = DecodeTime(start); + p.EndTime = DecodeTime(end); + } + } + } + if (p != null && !allNumbers && line.Length > 1) + { + line = line.Trim(); + if (line.StartsWith("}{}") || line.StartsWith("][]")) + line = line.Remove(0, 3); + sb.AppendLine(line.Trim()); + } + } + if (p != null) + { + p.Text = sb.ToString().Trim(); + subtitle.Paragraphs.Add(p); + } + + subtitle.Renumber(); + return subtitle; + } + + private static TimeCode DecodeTime(string[] parts) + { + try + { + string hour = parts[0]; + string minutes = parts[1]; + string seconds = parts[2]; + string frames; + if (parts.Length < 4) + { + frames = seconds; + seconds = minutes; + minutes = hour; + hour = "0"; + } + else + { + frames = parts[3]; + } + + if (frames.Length < 3) + return new TimeCode(int.Parse(hour), int.Parse(minutes), int.Parse(seconds), SubtitleFormat.FramesToMillisecondsMax999(int.Parse(frames))); + return new TimeCode(int.Parse(hour), int.Parse(minutes), int.Parse(seconds), int.Parse(frames)); + } + catch + { + return new TimeCode(0, 0, 0, 0); + } + } + + } +} diff --git a/libse/Utilities.cs b/libse/Utilities.cs new file mode 100644 index 000000000..0cde40c4a --- /dev/null +++ b/libse/Utilities.cs @@ -0,0 +1,3036 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Globalization; +using System.IO; +using System.Net; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using System.Windows.Forms; +using System.Xml; +using Nikse.Core; +using Nikse.SubtitleEdit.Core.ContainerFormats; +using Nikse.SubtitleEdit.Core.ContainerFormats.Matroska; +using Nikse.SubtitleEdit.Core.ContainerFormats.Mp4; +using Nikse.SubtitleEdit.Core.SubtitleFormats; + +namespace Nikse.SubtitleEdit.Core +{ + public static class Utilities + { + public const string WinXP2KUnicodeFontName = "Times New Roman"; + + /// + /// Cached environment new line characters for faster lookup. + /// + public static readonly char[] NewLineChars = Environment.NewLine.ToCharArray(); + + public static VideoInfo TryReadVideoInfoViaMatroskaHeader(string fileName) + { + var info = new VideoInfo { Success = false }; + + MatroskaFile matroska = null; + try + { + matroska = new MatroskaFile(fileName); + if (matroska.IsValid) + { + double frameRate; + int width; + int height; + double milliseconds; + string videoCodec; + matroska.GetInfo(out frameRate, out width, out height, out milliseconds, out videoCodec); + + info.Width = width; + info.Height = height; + info.FramesPerSecond = frameRate; + info.Success = true; + info.TotalMilliseconds = milliseconds; + info.TotalSeconds = milliseconds / TimeCode.BaseUnit; + info.TotalFrames = info.TotalSeconds * frameRate; + info.VideoCodec = videoCodec; + } + } + catch (Exception ex) + { + MessageBox.Show(ex.Message); + } + finally + { + if (matroska != null) + { + matroska.Dispose(); + } + } + + return info; + } + + public static VideoInfo TryReadVideoInfoViaAviHeader(string fileName) + { + var info = new VideoInfo { Success = false }; + + try + { + using (var rp = new RiffParser()) + { + var dh = new RiffDecodeHeader(rp); + rp.OpenFile(fileName); + info.FileType = RiffParser.FromFourCC(rp.FileType); + if (RiffParser.ckidAVI == rp.FileType) + { + dh.ProcessMainAVI(); + info.Width = dh.Width; + info.Height = dh.Height; + info.FramesPerSecond = dh.FrameRate; + info.TotalFrames = dh.TotalFrames; + info.TotalMilliseconds = dh.TotalMilliseconds; + info.TotalSeconds = info.TotalMilliseconds / TimeCode.BaseUnit; + info.VideoCodec = dh.VideoHandler; + info.Success = true; + } + } + } + catch + { + } + return info; + } + + public static VideoInfo TryReadVideoInfoViaMp4(string fileName) + { + var info = new VideoInfo { Success = false }; + + try + { + var mp4Parser = new MP4Parser(fileName); + if (mp4Parser.Moov != null && mp4Parser.VideoResolution.X > 0) + { + info.Width = mp4Parser.VideoResolution.X; + info.Height = mp4Parser.VideoResolution.Y; + info.TotalMilliseconds = mp4Parser.Duration.TotalSeconds; + info.VideoCodec = "MP4"; + info.FramesPerSecond = mp4Parser.FrameRate; + info.Success = true; + } + } + catch + { + } + return info; + } + + public static List GetMovieFileExtensions() + { + return new List { ".avi", ".mkv", ".wmv", ".mpg", ".mpeg", ".divx", ".mp4", ".asf", ".flv", ".mov", ".m4v", ".vob", ".ogv", ".webm", ".ts", ".m2ts", ".avs", ".mxf" }; + } + + public static string GetVideoFileFilter(bool includeAudioFiles) + { + var sb = new StringBuilder(); + sb.Append(Configuration.Settings.Language.General.VideoFiles + "|"); + int i = 0; + foreach (string extension in GetMovieFileExtensions()) + { + if (i > 0) + sb.Append(';'); + sb.Append('*'); + sb.Append(extension); + i++; + } + if (includeAudioFiles) + { + sb.Append('|'); + sb.Append(Configuration.Settings.Language.General.AudioFiles); + sb.Append("|*.mp3;*.wav;*.wma;*.ogg;*.mpa;*.m4a;*.ape;*.aiff;*.flac;*.aac"); + } + sb.Append('|'); + sb.Append(Configuration.Settings.Language.General.AllFiles); + sb.Append("|*.*"); + return sb.ToString(); + } + + public static bool IsInteger(string s) + { + int i; + return int.TryParse(s, out i); + } + + public static SubtitleFormat GetSubtitleFormatByFriendlyName(string friendlyName) + { + foreach (SubtitleFormat format in SubtitleFormat.AllSubtitleFormats) + { + if (format.FriendlyName == friendlyName || format.Name == friendlyName) + return format; + } + return null; + } + + public static string FormatBytesToDisplayFileSize(long fileSize) + { + if (fileSize <= 1024) + return string.Format("{0} bytes", fileSize); + if (fileSize <= 1024 * 1024) + return string.Format("{0} kb", fileSize / 1024); + if (fileSize <= 1024 * 1024 * 1024) + return string.Format("{0:0.0} mb", (float)fileSize / (1024 * 1024)); + return string.Format("{0:0.0} gb", (float)fileSize / (1024 * 1024 * 1024)); + } + + + + /// + /// Downloads the requested resource as a using the configured . + /// + /// A containing the URI to download. + /// Encoding for source text + /// A containing the requested resource. + public static string DownloadString(string address, Encoding encoding = null) + { + using (var wc = new WebClient()) + { + wc.Proxy = GetProxy(); + if (encoding != null) + wc.Encoding = encoding; + return wc.DownloadString(address).Trim(); + } + } + + public static WebProxy GetProxy() + { + if (!string.IsNullOrEmpty(Configuration.Settings.Proxy.ProxyAddress)) + { + var proxy = new WebProxy(Configuration.Settings.Proxy.ProxyAddress); + + if (!string.IsNullOrEmpty(Configuration.Settings.Proxy.UserName)) + { + if (string.IsNullOrEmpty(Configuration.Settings.Proxy.Domain)) + proxy.Credentials = new NetworkCredential(Configuration.Settings.Proxy.UserName, Configuration.Settings.Proxy.DecodePassword()); + else + proxy.Credentials = new NetworkCredential(Configuration.Settings.Proxy.UserName, Configuration.Settings.Proxy.DecodePassword(), Configuration.Settings.Proxy.Domain); + } + else + proxy.UseDefaultCredentials = true; + + return proxy; + } + return null; + } + + private static bool IsPartOfNumber(string s, int position) + { + if (string.IsNullOrWhiteSpace(s) || position + 1 >= s.Length) + return false; + + if (position > 0 && @",.".Contains(s[position])) + { + return char.IsDigit(s[position - 1]) && char.IsDigit(s[position + 1]); + } + return false; + } + + public static bool IsBetweenNumbers(string s, int position) + { + if (string.IsNullOrEmpty(s) || position < 1 || position + 2 > s.Length) + return false; + return char.IsDigit(s[position - 1]) && char.IsDigit(s[position + 1]); + } + + public static string AutoBreakLine(string text, string language) + { + return AutoBreakLine(text, Configuration.Settings.General.SubtitleLineMaximumLength, Configuration.Settings.Tools.MergeLinesShorterThan, language); + } + + public static string AutoBreakLine(string text) + { + return AutoBreakLine(text, string.Empty); // no language + } + + private static bool CanBreak(string s, int index, string language) + { + char nextChar = ' '; + if (index >= 0 && index < s.Length) + nextChar = s[index]; + else + return false; + if (!"\r\n\t ".Contains(nextChar)) + return false; + + // Some words we don't like breaking after + string s2 = s.Substring(0, index); + if (Configuration.Settings.Tools.UseNoLineBreakAfter) + { + foreach (NoBreakAfterItem ending in NoBreakAfterList(language)) + { + if (ending.IsMatch(s2)) + return false; + } + } + + if (s2.EndsWith("? -", StringComparison.Ordinal) || s2.EndsWith("! -", StringComparison.Ordinal) || s2.EndsWith(". -", StringComparison.Ordinal)) + return false; + + return true; + } + + private static string _lastNoBreakAfterListLanguage; + private static List _lastNoBreakAfterList = new List(); + private static IEnumerable NoBreakAfterList(string languageName) + { + if (string.IsNullOrEmpty(languageName)) + return new List(); + + if (languageName == _lastNoBreakAfterListLanguage) + return _lastNoBreakAfterList; + + _lastNoBreakAfterList = new List(); + + //load words via xml + string noBreakAfterFileName = DictionaryFolder + languageName + "_NoBreakAfterList.xml"; + var doc = new XmlDocument(); + if (File.Exists(noBreakAfterFileName)) + { + doc.Load(noBreakAfterFileName); + foreach (XmlNode node in doc.DocumentElement) + { + if (!string.IsNullOrEmpty(node.InnerText)) + { + if (node.Attributes["RegEx"] != null && node.Attributes["RegEx"].InnerText.Equals("true", StringComparison.OrdinalIgnoreCase)) + { + Regex r = new Regex(node.InnerText, RegexOptions.Compiled); + _lastNoBreakAfterList.Add(new NoBreakAfterItem(r, node.InnerText)); + } + else + { + _lastNoBreakAfterList.Add(new NoBreakAfterItem(node.InnerText)); + } + } + } + } + _lastNoBreakAfterListLanguage = languageName; + + return _lastNoBreakAfterList; + } + + public static string AutoBreakLineMoreThanTwoLines(string text, int maximumLineLength, string language) + { + if (text == null || text.Length < 3) + return text; + + string s = AutoBreakLine(text, 0, 0, language); + + var arr = s.SplitToLines(); + if ((arr.Length < 2 && arr[0].Length <= maximumLineLength) || (arr[0].Length <= maximumLineLength && arr[1].Length <= maximumLineLength)) + return s; + + s = RemoveLineBreaks(s); + + var htmlTags = new Dictionary(); + var sb = new StringBuilder(s.Length); + int six = 0; + while (six < s.Length) + { + var letter = s[six]; + var tagFound = letter == '<' && (s.Substring(six).StartsWith("', six + 1); + + if (tagFound && endIndex > 0) + { + string tag = s.Substring(six, endIndex - six + 1); + s = s.Remove(six, tag.Length); + if (htmlTags.ContainsKey(six)) + htmlTags[six] = htmlTags[six] + tag; + else + htmlTags.Add(six, tag); + } + else + { + sb.Append(letter); + six++; + } + } + s = sb.ToString(); + + var words = s.Split(' '); + for (int numberOfLines = 3; numberOfLines < 9999; numberOfLines++) + { + int average = s.Length / numberOfLines + 1; + for (int len = average; len < maximumLineLength; len++) + { + List list = SplitToX(words, numberOfLines, len); + bool allOk = true; + foreach (var lineLength in list) + { + if (lineLength > maximumLineLength) + allOk = false; + } + if (allOk) + { + int index = 0; + foreach (var item in list) + { + index += item; + htmlTags.Add(index, Environment.NewLine); + } + s = ReInsertHtmlTags(s, htmlTags); + s = s.Replace(" " + Environment.NewLine, Environment.NewLine); + s = s.Replace(Environment.NewLine + " ", Environment.NewLine); + s = s.Replace(Environment.NewLine + "
", "
" + Environment.NewLine); + s = s.Replace(Environment.NewLine + "", "" + Environment.NewLine); + s = s.Replace(Environment.NewLine + "", "" + Environment.NewLine); + s = s.Replace(Environment.NewLine + "", "" + Environment.NewLine); + return s.TrimEnd(); + } + } + } + + return text; + } + + private static List SplitToX(string[] words, int count, int average) + { + var list = new List(); + int currentIdx = 0; + int currentCount = 0; + foreach (string word in words) + { + if (currentCount + word.Length + 3 > average && currentIdx < count) + { + list.Add(currentCount); + currentIdx++; + currentCount = 0; + } + currentCount += word.Length + 1; + } + if (currentIdx < count) + list.Add(currentCount); + else + list[list.Count - 1] += currentCount; + return list; + } + + public static string AutoBreakLine(string text, int maximumLength, int mergeLinesShorterThan, string language) + { + if (text == null || text.Length < 3) + return text; + + // do not autobreak dialogs + if (text.Contains('-') && text.Contains(Environment.NewLine)) + { + var noTagLines = HtmlUtil.RemoveHtmlTags(text, true).SplitToLines(); + if (noTagLines.Length == 2) + { + var arr0 = noTagLines[0].Trim().TrimEnd('"').TrimEnd('\'').TrimEnd(); + if (arr0.StartsWith('-') && noTagLines[1].TrimStart().StartsWith('-') && arr0.Length > 1 && ".?!)]".Contains(arr0[arr0.Length - 1]) || arr0.EndsWith("--", StringComparison.Ordinal) || arr0.EndsWith('–')) + return text; + } + } + + string s = RemoveLineBreaks(text); + string noTagText = HtmlUtil.RemoveHtmlTags(s, true); + + if (noTagText.Length < mergeLinesShorterThan) + { + var noTagLines = noTagText.SplitToLines(); + if (noTagLines.Length > 1) + { + bool isDialog = true; + foreach (string line in noTagLines) + { + var noTagLine = line.TrimStart(); + isDialog = isDialog && (noTagLine.StartsWith('-') || noTagLine.StartsWith('—')); + } + if (isDialog) + { + return text; + } + } + return s; + } + + var htmlTags = new Dictionary(); + var sb = new StringBuilder(); + int six = 0; + while (six < s.Length) + { + var letter = s[six]; + var tagFound = letter == '<' && (s.Substring(six).StartsWith("', six + 1); + + if (tagFound && endIndex > 0) + { + string tag = s.Substring(six, endIndex - six + 1); + s = s.Remove(six, tag.Length); + if (htmlTags.ContainsKey(six)) + htmlTags[six] = htmlTags[six] + tag; + else + htmlTags.Add(six, tag); + } + else + { + sb.Append(letter); + six++; + } + } + s = sb.ToString(); + + int splitPos = -1; + int mid = s.Length / 2; + + // try to find " - " with uppercase letter after (dialog) + if (s.Contains(" - ")) + { + for (int j = 0; j <= (maximumLength / 2) + 5; j++) + { + if (mid + j + 4 < s.Length) + { + if (s[mid + j] == '-' && s[mid + j + 1] == ' ' && s[mid + j - 1] == ' ') + { + string rest = s.Substring(mid + j + 1).TrimStart(); + if (rest.Length > 0 && char.IsUpper(rest[0])) + { + splitPos = mid + j; + break; + } + } + } + if (mid - (j + 1) > 4) + { + if (s[mid - j] == '-' && s[mid - j + 1] == ' ' && s[mid - j - 1] == ' ') + { + string rest = s.Substring(mid - j + 1).TrimStart(); + if (rest.Length > 0 && char.IsUpper(rest[0])) + { + if (mid - j > 5 && s[mid - j - 1] == ' ' && @"!?.".Contains(s[mid - j - 2])) + { + splitPos = mid - j; + break; + } + } + } + } + } + } + + if (splitPos == maximumLength + 1 && s[maximumLength] != ' ') // only allow space for last char (as it does not count) + splitPos = -1; + + if (splitPos < 0) + { + for (int j = 0; j < 15; j++) + { + if (mid + j + 1 < s.Length && mid + j > 0) + { + if (@".!?".Contains(s[mid + j]) && !IsPartOfNumber(s, mid + j) && CanBreak(s, mid + j + 1, language)) + { + splitPos = mid + j + 1; + if (@".!?0123456789".Contains(s[splitPos])) + { // do not break double/tripple end lines like "!!!" or "..." + splitPos++; + if (@".!?0123456789".Contains(s[mid + j + 1])) + splitPos++; + } + break; + } + if (@".!?".Contains(s[mid - j]) && !IsPartOfNumber(s, mid - j) && CanBreak(s, mid - j, language)) + { + splitPos = mid - j; + splitPos++; + break; + } + } + } + } + + if (splitPos > maximumLength) // too long first line + { + if (splitPos != maximumLength + 1 || s[maximumLength] != ' ') // allow for maxlength+1 char to be space (does not count) + splitPos = -1; + } + else if (splitPos >= 0 && s.Length - splitPos > maximumLength) // too long second line + { + splitPos = -1; + } + + const string expectedChars1 = ".!?, "; + const string expectedChars2 = " .!?"; + const string expectedChars3 = ".!?"; + if (splitPos < 0) + { + for (int j = 0; j < 25; j++) + { + if (mid + j + 1 < s.Length && mid + j > 0) + { + if (expectedChars1.Contains(s[mid + j]) && !IsPartOfNumber(s, mid + j) && s.Length > mid + j + 2 && CanBreak(s, mid + j, language)) + { + splitPos = mid + j; + if (expectedChars2.Contains(s[mid + j + 1])) + { + splitPos++; + if (expectedChars2.Contains(s[mid + j + 2])) + splitPos++; + } + break; + } + if (expectedChars1.Contains(s[mid - j]) && !IsPartOfNumber(s, mid - j) && s.Length > mid + j + 2 && CanBreak(s, mid - j, language)) + { + splitPos = mid - j; + if (expectedChars3.Contains(s[splitPos])) + splitPos--; + if (expectedChars3.Contains(s[splitPos])) + splitPos--; + if (expectedChars3.Contains(s[splitPos])) + splitPos--; + break; + } + } + } + } + + if (splitPos < 0) + { + splitPos = mid; + s = s.Insert(mid - 1, Environment.NewLine); + s = ReInsertHtmlTags(s, htmlTags); + htmlTags = new Dictionary(); + s = s.Replace(Environment.NewLine, "-"); + } + if (splitPos < s.Length - 2) + s = s.Substring(0, splitPos) + Environment.NewLine + s.Substring(splitPos); + + s = ReInsertHtmlTags(s, htmlTags); + s = s.Replace(" " + Environment.NewLine, Environment.NewLine); + s = s.Replace(Environment.NewLine + " ", Environment.NewLine); + + return s.TrimEnd(); + } + + public static string RemoveLineBreaks(string s) + { + s = HtmlUtil.FixUpperTags(s); + s = s.Replace(Environment.NewLine + "", "" + Environment.NewLine); + s = s.Replace(Environment.NewLine + "", "" + Environment.NewLine); + s = s.Replace(Environment.NewLine + "", "" + Environment.NewLine); + s = s.Replace(Environment.NewLine + "", "" + Environment.NewLine); + s = s.Replace(" " + Environment.NewLine + "", " "); + s = s.Replace("" + Environment.NewLine + " ", " "); + s = s.Replace("" + Environment.NewLine + "", " "); + s = s.Replace(Environment.NewLine, " "); + s = s.Replace(" ", " "); + s = s.Replace(" ", " "); + s = s.Replace(" ", " "); + s = s.Replace(" ", " "); + s = s.FixExtraSpaces(); + return s.Trim(); + } + + private static string ReInsertHtmlTags(string s, Dictionary htmlTags) + { + if (htmlTags.Count > 0) + { + var sb = new StringBuilder(s.Length); + int six = 0; + foreach (var letter in s) + { + if (Environment.NewLine.Contains(letter)) + { + sb.Append(letter); + } + else + { + if (htmlTags.ContainsKey(six)) + { + sb.Append(htmlTags[six]); + } + sb.Append(letter); + six++; + } + } + if (htmlTags.ContainsKey(six)) + { + sb.Append(htmlTags[six]); + } + return sb.ToString(); + } + return s; + } + + public static string UnbreakLine(string text) + { + if (!text.Contains(Environment.NewLine)) + return text; + + var singleLine = text.Replace(Environment.NewLine, " "); + while (singleLine.Contains(" ")) + singleLine = singleLine.Replace(" ", " "); + + if (singleLine.Contains(" ", " "); + singleLine = singleLine.Replace("", " "); + + singleLine = singleLine.Replace(" ", " "); + singleLine = singleLine.Replace("", " "); + + singleLine = singleLine.Replace(" ", " "); + singleLine = singleLine.Replace("", " "); + } + return singleLine; + } + + public static void InitializeSubtitleFont(Control control) + { + var gs = Configuration.Settings.General; + + if (string.IsNullOrEmpty(gs.SubtitleFontName)) + gs.SubtitleFontName = "Tahoma"; + + try + { + if (gs.SubtitleFontBold) + control.Font = new Font(gs.SubtitleFontName, gs.SubtitleFontSize, FontStyle.Bold); + else + control.Font = new Font(gs.SubtitleFontName, gs.SubtitleFontSize); + + control.BackColor = gs.SubtitleBackgroundColor; + control.ForeColor = gs.SubtitleFontColor; + } + catch + { + } + } + + public static string RemoveSsaTags(string s) + { + int k = s.IndexOf('{'); + while (k >= 0) + { + int l = s.IndexOf('}', k); + if (l > k) + { + s = s.Remove(k, l - k + 1); + if (s.Length > 1 && s.Length > k) + k = s.IndexOf('{', k); + else + break; + } + else + { + break; + } + } + return s; + } + + public static Encoding GetEncodingFromFile(string fileName) + { + Encoding encoding = Encoding.Default; + + try + { + foreach (EncodingInfo ei in Encoding.GetEncodings()) + { + if (ei.CodePage + ": " + ei.DisplayName == Configuration.Settings.General.DefaultEncoding && + ei.Name != Encoding.UTF8.BodyName && + ei.Name != Encoding.Unicode.BodyName) + { + encoding = ei.GetEncoding(); + break; + } + } + + using (var file = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + var bom = new byte[12]; // Get the byte-order mark, if there is one + file.Position = 0; + file.Read(bom, 0, 12); + if (bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf) + encoding = Encoding.UTF8; + else if (bom[0] == 0xff && bom[1] == 0xfe) + encoding = Encoding.Unicode; + else if (bom[0] == 0xfe && bom[1] == 0xff) // utf-16 and ucs-2 + encoding = Encoding.BigEndianUnicode; + else if (bom[0] == 0 && bom[1] == 0 && bom[2] == 0xfe && bom[3] == 0xff) // ucs-4 + encoding = Encoding.UTF32; + else if (bom[0] == 0x2b && bom[1] == 0x2f && bom[2] == 0x76 && (bom[3] == 0x38 || bom[3] == 0x39 || bom[3] == 0x2b || bom[3] == 0x2f)) // utf-7 + encoding = Encoding.UTF7; + else if (file.Length > 12) + { + long length = file.Length; + if (length > 500000) + length = 500000; + + file.Position = 0; + var buffer = new byte[length]; + file.Read(buffer, 0, (int)length); + + bool couldBeUtf8; + if (IsUtf8(buffer, out couldBeUtf8)) + { + encoding = Encoding.UTF8; + } + else if (couldBeUtf8 && Configuration.Settings.General.DefaultEncoding == Encoding.UTF8.BodyName) + { // keep utf-8 encoding if it's default + encoding = Encoding.UTF8; + } + else if (couldBeUtf8 && fileName.EndsWith(".xml", StringComparison.OrdinalIgnoreCase) && Encoding.Default.GetString(buffer).ToLower().Replace('\'', '"').Contains("encoding=\"utf-8\"")) + { // keep utf-8 encoding for xml files with utf-8 in header (without any utf-8 encoded characters, but with only allowed utf-8 characters) + encoding = Encoding.UTF8; + } + else if (Configuration.Settings.General.AutoGuessAnsiEncoding) + { + encoding = DetectAnsiEncoding(buffer); + + Encoding greekEncoding = Encoding.GetEncoding(1253); // Greek + if (GetCount(greekEncoding.GetString(buffer), AutoDetectWordsGreek) > 5) + return greekEncoding; + + Encoding russianEncoding = Encoding.GetEncoding(1251); // Cyrillic + if (GetCount(russianEncoding.GetString(buffer), "что", "быть", "весь", "этот", "один", "такой") > 5) // Russian + return russianEncoding; + if (GetCount(russianEncoding.GetString(buffer), "Какво", "тук", "може", "Как", "Ваше", "какво") > 5) // Bulgarian + return russianEncoding; + russianEncoding = Encoding.GetEncoding(28595); // Russian + if (GetCount(russianEncoding.GetString(buffer), "что", "быть", "весь", "этот", "один", "такой") > 5) + return russianEncoding; + + Encoding thaiEncoding = Encoding.GetEncoding(874); // Thai + if (GetCount(thaiEncoding.GetString(buffer), "โอ", "โรเบิร์ต", "วิตตอเรีย", "ดร", "คุณตำรวจ", "ราเชล") + GetCount(thaiEncoding.GetString(buffer), "ไม่", "เลดดิส", "พระเจ้า", "เท็ดดี้", "หัวหน้า", "แอนดรูว์") > 5) + return thaiEncoding; + + Encoding arabicEncoding = Encoding.GetEncoding(28596); // Arabic + Encoding hewbrewEncoding = Encoding.GetEncoding(28598); // Hebrew + if (GetCount(arabicEncoding.GetString(buffer), "من", "هل", "لا", "فى", "لقد", "ما") > 5) + { + if (GetCount(hewbrewEncoding.GetString(buffer), "אולי", "אולי", "אולי", "אולי", "טוב", "טוב") > 10) + return hewbrewEncoding; + return arabicEncoding; + } + if (GetCount(hewbrewEncoding.GetString(buffer), AutoDetectWordsHebrew) > 5) + return hewbrewEncoding; + + Encoding romanianEncoding = Encoding.GetEncoding(1250); // Romanian + if (GetCount(romanianEncoding.GetString(buffer), "să", "şi", "văzut", "regulă", "găsit", "viaţă") > 99) + return romanianEncoding; + + Encoding koreanEncoding = Encoding.GetEncoding(949); // Korean + if (GetCount(koreanEncoding.GetString(buffer), "그리고", "아니야", "하지만", "말이야", "그들은", "우리가") > 5) + return koreanEncoding; + } + } + } + } + catch + { + } + return encoding; + } + + /// + /// Will try to determine if buffer is utf-8 encoded or not. + /// If any non-utf8 sequences are found then false is returned, if no utf8 multibytes sequences are found then false is returned. + /// + private static bool IsUtf8(byte[] buffer, out bool couldBeUtf8) + { + couldBeUtf8 = false; + int utf8Count = 0; + int i = 0; + while (i < buffer.Length - 3) + { + byte b = buffer[i]; + if (b > 127) + { + if (b >= 194 && b <= 223 && buffer[i + 1] >= 128 && buffer[i + 1] <= 191) + { // 2-byte sequence + utf8Count++; + i++; + } + else if (b >= 224 && b <= 239 && buffer[i + 1] >= 128 && buffer[i + 1] <= 191 && + buffer[i + 2] >= 128 && buffer[i + 2] <= 191) + { // 3-byte sequence + utf8Count++; + i += 2; + } + else if (b >= 240 && b <= 244 && buffer[i + 1] >= 128 && buffer[i + 1] <= 191 && + buffer[i + 2] >= 128 && buffer[i + 2] <= 191 && + buffer[i + 3] >= 128 && buffer[i + 3] <= 191) + { // 4-byte sequence + utf8Count++; + i += 3; + } + else + { + return false; + } + } + i++; + } + couldBeUtf8 = true; + if (utf8Count == 0) + return false; // not utf-8 (no characters utf-8 encoded...) + + return true; + } + + public static Encoding DetectAnsiEncoding(byte[] buffer) + { + if (IsRunningOnMono()) + return Encoding.Default; + + try + { + Encoding encoding =DetectEncoding.EncodingTools.DetectInputCodepage(buffer); + + Encoding greekEncoding = Encoding.GetEncoding(1253); // Greek + if (GetCount(greekEncoding.GetString(buffer), AutoDetectWordsGreek) > 5) + return greekEncoding; + + Encoding russianEncoding = Encoding.GetEncoding(1251); // Cyrillic + if (GetCount(russianEncoding.GetString(buffer), "что", "быть", "весь", "этот", "один", "такой") > 5) // Russian + return russianEncoding; + if (GetCount(russianEncoding.GetString(buffer), "Какво", "тук", "може", "Как", "Ваше", "какво") > 5) // Bulgarian + return russianEncoding; + + russianEncoding = Encoding.GetEncoding(28595); // Russian + if (GetCount(russianEncoding.GetString(buffer), "что", "быть", "весь", "этот", "один", "такой") > 5) // Russian + return russianEncoding; + + Encoding thaiEncoding = Encoding.GetEncoding(874); // Thai + if (GetCount(thaiEncoding.GetString(buffer), "โอ", "โรเบิร์ต", "วิตตอเรีย", "ดร", "คุณตำรวจ", "ราเชล") + GetCount(thaiEncoding.GetString(buffer), "ไม่", "เลดดิส", "พระเจ้า", "เท็ดดี้", "หัวหน้า", "แอนดรูว์") > 5) + return thaiEncoding; + + Encoding arabicEncoding = Encoding.GetEncoding(28596); // Arabic + Encoding hewbrewEncoding = Encoding.GetEncoding(28598); // Hebrew + if (GetCount(arabicEncoding.GetString(buffer), "من", "هل", "لا", "فى", "لقد", "ما") > 5) + { + if (GetCount(hewbrewEncoding.GetString(buffer), "אולי", "אולי", "אולי", "אולי", "טוב", "טוב") > 10) + return hewbrewEncoding; + return arabicEncoding; + } + if (GetCount(hewbrewEncoding.GetString(buffer), AutoDetectWordsHebrew) > 5) + return hewbrewEncoding; + + return encoding; + } + catch + { + return Encoding.Default; + } + } + + public static string DictionaryFolder + { + get + { + return Configuration.DictionariesFolder; + } + } + + public static List GetDictionaryLanguages() + { + var list = new List(); + if (Directory.Exists(DictionaryFolder)) + { + foreach (string dic in Directory.GetFiles(DictionaryFolder, "*.dic")) + { + string name = Path.GetFileNameWithoutExtension(dic); + if (!name.StartsWith("hyph", StringComparison.Ordinal)) + { + try + { + var ci = CultureInfo.GetCultureInfo(name.Replace('_', '-')); + name = ci.DisplayName + " [" + name + "]"; + } + catch (Exception exception) + { + System.Diagnostics.Debug.WriteLine(exception.Message); + name = "[" + name + "]"; + } + list.Add(name); + } + } + } + return list; + } + + public static double GetOptimalDisplayMilliseconds(string text) + { + return GetOptimalDisplayMilliseconds(text, Configuration.Settings.General.SubtitleOptimalCharactersPerSeconds); + } + + public static double GetOptimalDisplayMilliseconds(string text, double optimalCharactersPerSecond) + { + if (optimalCharactersPerSecond < 2 || optimalCharactersPerSecond > 100) + optimalCharactersPerSecond = 14.7; + double duration = (HtmlUtil.RemoveHtmlTags(text, true).Length / optimalCharactersPerSecond) * TimeCode.BaseUnit; + + if (duration < Configuration.Settings.General.SubtitleMinimumDisplayMilliseconds) + duration = Configuration.Settings.General.SubtitleMinimumDisplayMilliseconds; + + if (duration > Configuration.Settings.General.SubtitleMaximumDisplayMilliseconds) + duration = Configuration.Settings.General.SubtitleMaximumDisplayMilliseconds; + + return duration; + } + + private static int GetCount(string text, params string[] words) + { + int count = 0; + for (int i = 0; i < words.Length; i++) + { + count += Regex.Matches(text, "\\b" + words[i] + "\\b", (RegexOptions.CultureInvariant | RegexOptions.ExplicitCapture)).Count; + } + return count; + } + + private static int GetCountContains(string text, params string[] words) + { + int count = 0; + for (int i = 0; i < words.Length; i++) + { + var regEx = new Regex(words[i]); + count += regEx.Matches(text).Count; + } + return count; + } + + public static string AutoDetectGoogleLanguage(Encoding encoding) + { + switch (encoding.CodePage) + { + case 860: + return "pt"; // Portuguese + case 28599: + case 1254: + return "tr"; // Turkish + case 28598: + case 1255: + return "he"; // Hebrew + case 28596: + case 1256: + return "ar"; // Arabic + case 1258: + return "vi"; // Vietnamese + case 949: + case 1361: + case 20949: + case 51949: + case 50225: + return "ko"; // Korean + case 1253: + case 28597: + return "el"; // Greek + case 50220: + case 50221: + case 50222: + case 51932: + case 20932: + case 10001: + return "ja"; // Japanese + case 20000: + case 20002: + case 20936: + case 950: + case 52936: + case 54936: + case 51936: + return "zh"; // Chinese + default: + return null; + } + } + + public static readonly string[] AutoDetectWordsEnglish = { "we", "are", "and", "you", "your", "what" }; + public static readonly string[] AutoDetectWordsDanish = { "vi", "han", "og", "jeg", "var", "men", "gider", "bliver", "virkelig", "kommer", "tilbage", "Hej" }; + public static readonly string[] AutoDetectWordsNorwegian = { "vi", "er", "og", "jeg", "var", "men" }; + public static readonly string[] AutoDetectWordsSwedish = { "vi", "är", "och", "Jag", "inte", "för" }; + public static readonly string[] AutoDetectWordsSpanish = { "el", "bien", "Vamos", "Hola", "casa", "con" }; + public static readonly string[] AutoDetectWordsFrench = { "un", "vous", "avec", "pas", "ce", "une" }; + public static readonly string[] AutoDetectWordsGerman = { "und", "auch", "sich", "bin", "hast", "möchte" }; + public static readonly string[] AutoDetectWordsDutch = { "van", "een", "[Hh]et", "m(ij|ij)", "z(ij|ij)n" }; + public static readonly string[] AutoDetectWordsPolish = { "Czy", "ale", "ty", "siê", "jest", "mnie" }; + public static readonly string[] AutoDetectWordsItalian = { "Cosa", "sono", "Grazie", "Buongiorno", "bene", "questo", "ragazzi", "propriamente", "numero", "hanno", "giorno", "faccio", "davvero", "negativo", "essere", "vuole", "sensitivo", "venire" }; + public static readonly string[] AutoDetectWordsPortuguese = { "não", "Não", "Estás", "Então", "isso", "com" }; + public static readonly string[] AutoDetectWordsGreek = { "μου", "είναι", "Είναι", "αυτό", "Τόμπυ", "καλά", "Ενταξει", "Ενταξει", "πρεπει", "Λοιπον", "τιποτα", "ξερεις" }; + public static readonly string[] AutoDetectWordsRussian = { "все", "это", "как", "Воробей", "сюда", "Давай" }; + public static readonly string[] AutoDetectWordsBulgarian = { "Какво", "тук", "може", "Как", "Ваше", "какво" }; + public static readonly string[] AutoDetectWordsRomanian = { "Какво", "тук", "може", "Как", "Ваше", "какво" }; + public static readonly string[] AutoDetectWordsArabic = { "Какво", "тук", "може", "Как", "Ваше", "какво" }; + public static readonly string[] AutoDetectWordsHebrew = { "אתה", "אולי", "הוא", "בסדר", "יודע", "טוב" }; + public static readonly string[] AutoDetectWordsVietnamese = { "không", "tôi", "anh", "đó", "Tôi", "ông" }; + public static readonly string[] AutoDetectWordsHungarian = { "hogy", "lesz", "tudom", "vagy", "mondtam", "még" }; + public static readonly string[] AutoDetectWordsTurkish = { "için", "Tamam", "Hayır", "benim", "daha", "deðil", "önce", "lazým", "benim", "çalýþýyor", "burada", "efendim" }; + public static readonly string[] AutoDetectWordsCroatianAndSerbian = { "sam", "ali", "nije", "samo", "ovo", "kako", "dobro", "sve", "tako", "će", "mogu", "ću", "zašto", "nešto", "za" }; + public static readonly string[] AutoDetectWordsCroatian = { "što", "ovdje", "gdje", "kamo", "tko", "prije", "uvijek", "vrijeme", "vidjeti", "netko", + "vidio", "nitko", "bok", "lijepo", "oprosti", "htio", "mjesto", "oprostite", "čovjek", "dolje", + "čovječe", "dvije", "dijete", "dio", "poslije", "događa", "vjerovati", "vjerojatno", "vjerujem", "točno", + "razumijem", "vidjela", "cijeli", "svijet", "obitelj", "volio", "sretan", "dovraga", "svijetu", "htjela", + "vidjeli", "negdje", "želio", "ponovno", "djevojka", "umrijeti", "čovjeka", "mjesta", "djeca", "osjećam", + "uopće", "djecu", "naprijed", "obitelji", "doista", "mjestu", "lijepa", "također", "riječ", "tijelo" }; + public static readonly string[] AutoDetectWordsSerbian = { "šta", "ovde", "gde", "ko", "pre", "uvek", "vreme", "videti", "neko", + "video", "niko", "ćao", "lepo", "izvini", "hteo", "mesto", "izvinite", "čovek", "dole", + "čoveče", "dve", "dete", "deo", "posle", "dešava", "verovati", "verovatno", "verujem", "tačno", + "razumem", "videla", "ceo", "svet", "porodica", "voleo", "srećan", "dođavola", "svetu", "htela", + "videli", "negde", "želeo", "ponovo", "devojka", "umreti", "čoveka", "mesta", "deca", "osećam", + "uopšte", "decu", "napred", "porodicu", "zaista", "mestu", "lepa", "takođe", "reč", "telo" }; + + public static string AutoDetectGoogleLanguage(string text, int bestCount) + { + int count = GetCount(text, AutoDetectWordsEnglish); + if (count > bestCount) + return "en"; + + count = GetCount(text, AutoDetectWordsDanish); + if (count > bestCount) + { + int norwegianCount = GetCount(text, "ut", "deg", "meg", "merkelig", "mye", "spørre"); + int dutchCount = GetCount(text, "van", "een", "[Hh]et", "m(ij|ij)", "z(ij|ij)n"); + if (norwegianCount < 2 && dutchCount < count) + return "da"; + } + + count = GetCount(text, AutoDetectWordsNorwegian); + if (count > bestCount) + { + int danishCount = GetCount(text, "siger", "dig", "mig", "mærkelig", "tilbage", "spørge"); + int dutchCount = GetCount(text, "van", "een", "[Hh]et", "m(ij|ij)", "z(ij|ij)n"); + if (danishCount < 2 && dutchCount < count) + return "no"; + } + + count = GetCount(text, AutoDetectWordsSwedish); + if (count > bestCount) + return "sv"; + + count = GetCount(text, AutoDetectWordsSpanish); + if (count > bestCount) + { + int frenchCount = GetCount(text, "[Cc]'est", "pas", "vous", "pour", "suis", "Pourquoi", "maison", "souviens", "quelque"); // not spanish words + if (frenchCount < 2) + return "es"; + } + + count = GetCount(text, AutoDetectWordsFrench); + if (count > bestCount) + { + int spanishCount = GetCount(text, "Hola", "nada", "Vamos", "pasa", "los", "como"); // not french words + int italianCount = GetCount(text, AutoDetectWordsItalian); + int romanianCount = GetCount(text, "sînt", "aici", "Sînt", "domnule", "pentru", "Vreau"); + if (spanishCount < 2 && italianCount < 2 && romanianCount < 5) + return "fr"; + } + + count = GetCount(text, AutoDetectWordsGerman); + if (count > bestCount) + return "de"; + + count = GetCount(text, AutoDetectWordsDutch); + if (count > bestCount) + return "nl"; + + count = GetCount(text, AutoDetectWordsPolish); + if (count > bestCount) + return "pl"; + + count = GetCount(text, AutoDetectWordsItalian); + if (count > bestCount) + { + int frenchCount = GetCount(text, "[Cc]'est", "pas", "vous", "pour", "suis", "Pourquoi", "maison", "souviens", "quelque"); // not spanish words + int spanishCount = GetCount(text, "Hola", "nada", "Vamos", "pasa", "los", "como"); // not french words + if (frenchCount < 2 && spanishCount < 2) + return "it"; + } + + count = GetCount(text, AutoDetectWordsPortuguese); + if (count > bestCount) + return "pt"; // Portuguese + + count = GetCount(text, AutoDetectWordsGreek); + if (count > bestCount) + return "el"; // Greek + + count = GetCount(text, AutoDetectWordsRussian); + if (count > bestCount) + return "ru"; // Russian + + count = GetCount(text, AutoDetectWordsBulgarian); + if (count > bestCount) + return "bg"; // Bulgarian + + count = GetCount(text, AutoDetectWordsArabic); + if (count > bestCount) + { + if (GetCount(text, "אולי", "אולי", "אולי", "אולי", "טוב", "טוב") > 10) + return "he"; + + int romanianCount = GetCount(text, "sînt", "aici", "Sînt", "domnule", "pentru", "Vreau", "trãiascã", "niciodatã", "înseamnã", + "vorbesti", "oamenii", "Asteaptã", "fãcut", "Fãrã", "spune", "decât", "pentru", "vreau"); + if (romanianCount > count) + return "ro"; // Romanian + + romanianCount = GetCount(text, "daca", "pentru", "acum", "soare", "trebuie", "Trebuie", "nevoie", "decat", "echilibrul", + "vorbesti", "oamenii", "zeului", "vrea", "atunci", "Poate", "Acum", "memoria", "soarele"); + if (romanianCount > count) + return "ro"; // Romanian + + return "ar"; // Arabic + } + + count = GetCount(text, AutoDetectWordsHebrew); + if (count > bestCount) + return "he"; // Hebrew + + count = GetCount(text, AutoDetectWordsCroatianAndSerbian); + if (count > bestCount) + { + int croatianCount = GetCount(text, AutoDetectWordsCroatian); + int serbianCount = GetCount(text, AutoDetectWordsSerbian); + if (croatianCount > serbianCount) + return "hr"; // Croatian + + return "sr"; // Serbian + } + + count = GetCount(text, AutoDetectWordsVietnamese); + if (count > bestCount) + return "vi"; // Vietnamese + + count = GetCount(text, AutoDetectWordsHungarian); + if (count > bestCount) + return "hu"; // Hungarian + + count = GetCount(text, AutoDetectWordsTurkish); + if (count > bestCount) + return "tr"; // Turkish + + count = GetCount(text, "yang", "tahu", "bisa", "akan", "tahun", "tapi", "dengan", "untuk", "rumah", "dalam", "sudah", "bertemu"); + if (count > bestCount) + return "id"; // Indonesian + + count = GetCount(text, "โอ", "โรเบิร์ต", "วิตตอเรีย", "ดร", "คุณตำรวจ", "ราเชล", "ไม่", "เลดดิส", "พระเจ้า", "เท็ดดี้", "หัวหน้า", "แอนดรูว์"); + if (count > 10 || count > bestCount) + return "th"; // Thai + + count = GetCount(text, "그리고", "아니야", "하지만", "말이야", "그들은", "우리가"); + if (count > 10 || count > bestCount) + return "ko"; // Korean + + count = GetCount(text, "että", "kuin", "minä", "mitään", "Mutta", "siitä", "täällä", "poika", "Kiitos", "enää", "vielä", "tässä"); + if (count > bestCount) + return "fi"; // Finnish + + count = GetCount(text, "sînt", "aici", "Sînt", "domnule", "pentru", "Vreau", "trãiascã", "niciodatã", "înseamnã", "vorbesti", "oamenii", + "Asteaptã", "fãcut", "Fãrã", "spune", "decât", "pentru", "vreau"); + if (count > bestCount) + return "ro"; // Romanian + + count = GetCount(text, "daca", "pentru", "acum", "soare", "trebuie", "Trebuie", "nevoie", "decat", "echilibrul", "vorbesti", "oamenii", + "zeului", "vrea", "atunci", "Poate", "Acum", "memoria", "soarele"); + if (count > bestCount) + return "ro"; // Romanian + + count = GetCountContains(text, "シ", "ュ", "シン", "シ", "ン", "ユ"); + count += GetCountContains(text, "イ", "ン", "チ", "ェ", "ク", "ハ"); + count += GetCountContains(text, "シ", "ュ", "う", "シ", "ン", "サ"); + count += GetCountContains(text, "シ", "ュ", "シ", "ン", "だ", "う"); + if (count > bestCount * 2) + return "ja"; // Japanese - not tested... + + count = GetCountContains(text, "是", "是早", "吧", "的", "爱", "上好"); + count += GetCountContains(text, "的", "啊", "好", "好", "亲", "的"); + count += GetCountContains(text, "谢", "走", "吧", "晚", "上", "好"); + count += GetCountContains(text, "来", "卡", "拉", "吐", "滚", "他"); + if (count > bestCount * 2) + return "zh"; // Chinese (simplified) - not tested... + + return string.Empty; + } + + public static string AutoDetectGoogleLanguage(Subtitle subtitle) + { + string languageId = AutoDetectGoogleLanguageOrNull(subtitle); + if (languageId == null) + languageId = "en"; + + return languageId; + } + + public static string AutoDetectGoogleLanguageOrNull(Subtitle subtitle) + { + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + sb.AppendLine(p.Text); + + string languageId = AutoDetectGoogleLanguage(sb.ToString(), subtitle.Paragraphs.Count / 14); + if (string.IsNullOrEmpty(languageId)) + languageId = null; + + return languageId; + } + + public static string AutoDetectLanguageName(string languageName, Subtitle subtitle) + { + if (string.IsNullOrEmpty(languageName)) + languageName = "en_US"; + int bestCount = subtitle.Paragraphs.Count / 14; + + var sb = new StringBuilder(); + foreach (Paragraph p in subtitle.Paragraphs) + sb.AppendLine(p.Text); + string text = sb.ToString(); + + List dictionaryNames = GetDictionaryLanguages(); + + bool containsEnGb = false; + bool containsEnUs = false; + bool containsHrHr = false; + bool containsSrLatn = false; + foreach (string name in dictionaryNames) + { + if (name.Contains("[en_GB]")) + containsEnGb = true; + if (name.Contains("[en_US]")) + containsEnUs = true; + if (name.Contains("[hr_HR]")) + containsHrHr = true; + if (name.Contains("[sr-Latn]")) + containsSrLatn = true; + } + + foreach (string name in dictionaryNames) + { + string shortName = string.Empty; + int start = name.IndexOf('['); + int end = name.IndexOf(']'); + if (start > 0 && end > start) + { + start++; + shortName = name.Substring(start, end - start); + } + + int count; + switch (shortName) + { + case "da_DK": + count = GetCount(text, "vi", "hun", "og", "jeg", "var", "men", "bliver", "meget", "spørger", "Hej", "utrolig", "dejligt"); + if (count > bestCount) + { + int norweigianCount = GetCount(text, "ut", "deg", "meg", "merkelig", "mye", "spørre"); + if (norweigianCount < 2) + languageName = shortName; + } + break; + case "nb_NO": + count = GetCount(text, AutoDetectWordsNorwegian); + if (count > bestCount) + { + int danishCount = GetCount(text, "siger", "dig", "mig", "mærkelig", "tilbage", "spørge"); + int dutchCount = GetCount(text, "van", "een", "[Hh]et", "m(ij|ij)", "z(ij|ij)n"); + if (danishCount < 2 && dutchCount < count) + languageName = shortName; + } + break; + case "en_US": + count = GetCount(text, AutoDetectWordsEnglish); + if (count > bestCount) + { + languageName = shortName; + if (containsEnGb) + { + int usCount = GetCount(text, "color", "flavor", "honor", "humor", "neighbor", "honor"); + int gbCount = GetCount(text, "colour", "flavour", "honour", "humour", "neighbour", "honour"); + if (gbCount > usCount) + languageName = "en_GB"; + } + } + break; + case "en_GB": + count = GetCount(text, "we", "are", "and", "you", "your", "what"); + if (count > bestCount) + { + languageName = shortName; + if (containsEnUs) + { + int usCount = GetCount(text, "color", "flavor", "honor", "humor", "neighbor", "honor"); + int gbCount = GetCount(text, "colour", "flavour", "honour", "humour", "neighbour", "honour"); + if (gbCount < usCount) + languageName = "en_US"; + } + } + break; + case "sv_SE": + count = GetCount(text, "vi", "är", "och", "Jag", "inte", "för"); + if (count > bestCount) + languageName = shortName; + break; + case "es_ES": + count = GetCount(text, AutoDetectWordsSpanish); + if (count > bestCount) + { + int frenchWords = GetCount(text, "[Cc]'est", "pas", "vous", "pour", "suis", "Pourquoi", "maison", "souviens", "quelque"); // not spanish words + if (frenchWords < 2) + languageName = shortName; + } + break; + case "fr_FR": + count = GetCount(text, AutoDetectWordsFrench); + if (count > bestCount) + { + int spanishWords = GetCount(text, "Hola", "nada", "Vamos", "pasa", "los", "como"); // not french words + int italianWords = GetCount(text, AutoDetectWordsItalian); // not italian words + if (spanishWords < 2 && italianWords < 2) + languageName = shortName; + } + break; + case "it_IT": + count = GetCount(text, AutoDetectWordsItalian); + if (count > bestCount) + { + int frenchWords = GetCount(text, "[Cc]'est", "pas", "vous", "pour", "suis", "Pourquoi", "maison", "souviens", "quelque"); // not spanish words + int spanishWords = GetCount(text, "Hola", "nada", "Vamos", "pasa", "los", "como"); // not french words + if (frenchWords < 2 && spanishWords < 2) + languageName = shortName; + } + break; + case "de_DE": + count = GetCount(text, "und", "auch", "sich", "bin", "hast", "möchte"); + if (count > bestCount) + languageName = shortName; + break; + case "nl_NL": + count = GetCount(text, "van", "een", "[Hh]et", "m(ij|ij)", "z(ij|ij)n"); + if (count > bestCount) + languageName = shortName; + break; + case "pl_PL": + count = GetCount(text, "Czy", "ale", "ty", "siê", "jest", "mnie"); + if (count > bestCount) + languageName = shortName; + break; + case "el_GR": + count = GetCount(text, AutoDetectWordsGreek); + if (count > bestCount) + languageName = shortName; + break; + case "ru_RU": + count = GetCount(text, AutoDetectWordsRussian); + if (count > bestCount) + languageName = shortName; + break; + case "ro_RO": + count = GetCount(text, "sînt", "aici", "Sînt", "domnule", "pentru", "Vreau", "trãiascã", "niciodatã", "înseamnã", "vorbesti", "oamenii", "Asteaptã", + "fãcut", "Fãrã", "spune", "decât", "pentru", "vreau"); + if (count > bestCount) + { + languageName = shortName; + } + else + { + count = GetCount(text, "daca", "pentru", "acum", "soare", "trebuie", "Trebuie", "nevoie", "decat", "echilibrul", "vorbesti", "oamenii", "zeului", + "vrea", "atunci", "Poate", "Acum", "memoria", "soarele"); + + if (count > bestCount) + languageName = shortName; + } + break; + case "hr_HR": // Croatian + count = GetCount(text, AutoDetectWordsCroatianAndSerbian); + if (count > bestCount) + { + languageName = shortName; + if (containsSrLatn) + { + int croatianCount = GetCount(text, AutoDetectWordsCroatian); + int serbianCount = GetCount(text, AutoDetectWordsSerbian); + if (serbianCount > croatianCount) + languageName = "sr-Latn"; + } + } + break; + case "sr-Latn": // Serbian (Latin) + count = GetCount(text, AutoDetectWordsCroatianAndSerbian); + if (count > bestCount) + { + languageName = shortName; + if (containsHrHr) + { + int croatianCount = GetCount(text, AutoDetectWordsCroatian); + int serbianCount = GetCount(text, AutoDetectWordsSerbian); + if (serbianCount < croatianCount) + languageName = "hr_HR"; + } + } + break; + case "pt_PT": // Portuguese + count = GetCount(text, AutoDetectWordsPortuguese); + if (count > bestCount) + languageName = shortName; + break; + case "pt_BR": // Portuguese (Brasil) + count = GetCount(text, AutoDetectWordsPortuguese); + if (count > bestCount) + languageName = shortName; + break; + case "hu_HU": // Hungarian + count = GetCount(text, AutoDetectWordsHungarian); + if (count > bestCount) + languageName = shortName; + break; + } + } + return languageName; + } + + public static string ColorToHex(Color c) + { + return string.Format("#{0:x2}{1:x2}{2:x2}", c.R, c.G, c.B); + } + + public static int GetMaxLineLength(string text) + { + int maxLength = 0; + text = HtmlUtil.RemoveHtmlTags(text, true); + foreach (string line in text.SplitToLines()) + { + if (line.Length > maxLength) + maxLength = line.Length; + } + return maxLength; + } + + public static double GetCharactersPerSecond(Paragraph paragraph) + { + if (paragraph.Duration.TotalMilliseconds < 1) + return 999; + + const string zeroWidthSpace = "\u200B"; + const string zeroWidthNoBreakSpace = "\uFEFF"; + + string s = HtmlUtil.RemoveHtmlTags(paragraph.Text, true).Replace(Environment.NewLine, string.Empty).Replace(zeroWidthSpace, string.Empty).Replace(zeroWidthNoBreakSpace, string.Empty); + return s.Length / paragraph.Duration.TotalSeconds; + } + + public static bool IsRunningOnMono() + { + return Type.GetType("Mono.Runtime") != null; + } + + public static void ShowHelp(string parameter) + { + string helpFile = Configuration.Settings.Language.General.HelpFile; + if (string.IsNullOrEmpty(helpFile)) + helpFile = "http://www.nikse.dk/SubtitleEdit/Help"; + System.Diagnostics.Process.Start(helpFile + parameter); + } + + public static string AssemblyVersion + { + get + { + return Assembly.GetExecutingAssembly().GetName().Version.ToString(); + } + } + + public static string AssemblyDescription + { + get + { + Assembly assy = Assembly.GetExecutingAssembly(); + String assyName = assy.GetName().Name; + bool isdef = Attribute.IsDefined(assy, typeof(AssemblyDescriptionAttribute)); + if (isdef) + { + Console.WriteLine(assyName); + var adAttr = (AssemblyDescriptionAttribute)Attribute.GetCustomAttribute(assy, typeof(AssemblyDescriptionAttribute)); + if (adAttr != null) + return adAttr.Description; + } + return null; + } + } + + private static void AddExtension(StringBuilder sb, string extension) + { + if (!sb.ToString().Contains("*" + extension + ";", StringComparison.OrdinalIgnoreCase)) + { + sb.Append('*'); + sb.Append(extension.TrimStart('*')); + sb.Append(';'); + } + } + + public static string GetOpenDialogFilter() + { + var sb = new StringBuilder(); + sb.Append(Configuration.Settings.Language.General.SubtitleFiles + "|"); + foreach (SubtitleFormat s in SubtitleFormat.AllSubtitleFormats) + { + AddExtension(sb, s.Extension); + foreach (string ext in s.AlternateExtensions) + AddExtension(sb, ext); + } + AddExtension(sb, new Pac().Extension); + AddExtension(sb, new Cavena890().Extension); + AddExtension(sb, new Spt().Extension); + AddExtension(sb, new Wsb().Extension); + AddExtension(sb, new CheetahCaption().Extension); + AddExtension(sb, ".chk"); + AddExtension(sb, new CaptionsInc().Extension); + AddExtension(sb, new Ultech130().Extension); + AddExtension(sb, new ELRStudioClosedCaption().Extension); + AddExtension(sb, ".uld"); // Ultech drop frame + AddExtension(sb, new SonicScenaristBitmaps().Extension); + AddExtension(sb, ".mks"); + AddExtension(sb, ".mxf"); + AddExtension(sb, ".sup"); + AddExtension(sb, ".dost"); + AddExtension(sb, new Ayato().Extension); + AddExtension(sb, new PacUnicode().Extension); + + if (!string.IsNullOrEmpty(Configuration.Settings.General.OpenSubtitleExtraExtensions)) + { + var extraExtensions = Configuration.Settings.General.OpenSubtitleExtraExtensions.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); + foreach (string ext in extraExtensions) + { + if (ext.StartsWith("*.", StringComparison.Ordinal) && !sb.ToString().Contains(ext, StringComparison.OrdinalIgnoreCase)) + AddExtension(sb, ext); + } + } + AddExtension(sb, ".son"); + + sb.Append('|'); + sb.Append(Configuration.Settings.Language.General.AllFiles); + sb.Append("|*.*"); + return sb.ToString(); + } + + public static void SetSaveDialogFilter(SaveFileDialog saveFileDialog, SubtitleFormat currentFormat) + { + var sb = new StringBuilder(); + int index = 0; + foreach (SubtitleFormat format in SubtitleFormat.AllSubtitleFormats) + { + sb.Append(format.Name + "|*" + format.Extension + "|"); + if (currentFormat.Name == format.Name) + saveFileDialog.FilterIndex = index + 1; + index++; + } + saveFileDialog.Filter = sb.ToString().TrimEnd('|'); + } + + public static Color ColorDarkOrange = Color.FromArgb(220, 90, 10); + + public static void GetLineLengths(Label label, string text) + { + label.ForeColor = Color.Black; + var lines = HtmlUtil.RemoveHtmlTags(text, true).SplitToLines(); + + const int max = 3; + + var sb = new StringBuilder(); + for (int i = 0; i < lines.Length; i++) + { + string line = lines[i]; + if (i > 0) + { + sb.Append('/'); + } + + if (i > max) + { + label.ForeColor = Color.Red; + sb.Append("..."); + label.Text = sb.ToString(); + return; + } + + sb.Append(line.Length); + if (line.Length > Configuration.Settings.General.SubtitleLineMaximumLength) + label.ForeColor = Color.Red; + else if (line.Length > Configuration.Settings.General.SubtitleLineMaximumLength - 5) + label.ForeColor = ColorDarkOrange; + } + label.Text = sb.ToString(); + } + + public static bool IsValidRegex(string testPattern) + { + if (string.IsNullOrEmpty(testPattern)) + { + return false; + } + + try + { + Regex.Match("", testPattern); + } + catch (ArgumentException) + { + // BAD PATTERN: Syntax error + return false; + } + return true; + } + + public static Regex MakeWordSearchRegex(string word) + { + string s = word.Replace("\\", "\\\\"); + s = s.Replace("*", "\\*"); + s = s.Replace(".", "\\."); + s = s.Replace("?", "\\?"); + return new Regex(@"\b" + s + @"\b", RegexOptions.Compiled); + } + + public static Regex MakeWordSearchRegexWithNumbers(string word) + { + string s = word.Replace("\\", "\\\\"); + s = s.Replace("*", "\\*"); + s = s.Replace(".", "\\."); + s = s.Replace("?", "\\?"); + return new Regex(@"[\b ,\.\?\!]" + s + @"[\b !\.,\r\n\?]", RegexOptions.Compiled); + } + + public static void RemoveFromUserDictionary(string word, string languageName) + { + word = word.Trim(); + if (word.Length > 0) + { + string userWordsXmlFileName = DictionaryFolder + languageName + "_user.xml"; + var userWords = new XmlDocument(); + if (File.Exists(userWordsXmlFileName)) + userWords.Load(userWordsXmlFileName); + else + userWords.LoadXml(""); + + var words = new List(); + foreach (XmlNode node in userWords.DocumentElement.SelectNodes("word")) + { + string w = node.InnerText.Trim(); + if (w.Length > 0 && w != word) + words.Add(w); + } + words.Sort(); + + userWords.DocumentElement.RemoveAll(); + foreach (string w in words) + { + XmlNode node = userWords.CreateElement("word"); + node.InnerText = w; + userWords.DocumentElement.AppendChild(node); + } + userWords.Save(userWordsXmlFileName); + } + } + + public static void AddToUserDictionary(string word, string languageName) + { + word = word.Trim(); + if (word.Length > 0) + { + string userWordsXmlFileName = DictionaryFolder + languageName + "_user.xml"; + var userWords = new XmlDocument(); + if (File.Exists(userWordsXmlFileName)) + userWords.Load(userWordsXmlFileName); + else + userWords.LoadXml(""); + + var words = new List(); + foreach (XmlNode node in userWords.DocumentElement.SelectNodes("word")) + { + string w = node.InnerText.Trim(); + if (w.Length > 0) + words.Add(w); + } + words.Add(word); + words.Sort(); + + userWords.DocumentElement.RemoveAll(); + foreach (string w in words) + { + XmlNode node = userWords.CreateElement("word"); + node.InnerText = w; + userWords.DocumentElement.AppendChild(node); + } + userWords.Save(userWordsXmlFileName); + } + } + + public static string LoadUserWordList(List userWordList, string languageName) + { + userWordList.Clear(); + var userWordDictionary = new XmlDocument(); + string userWordListXmlFileName = DictionaryFolder + languageName + "_user.xml"; + if (File.Exists(userWordListXmlFileName)) + { + userWordDictionary.Load(userWordListXmlFileName); + foreach (XmlNode node in userWordDictionary.DocumentElement.SelectNodes("word")) + { + string s = node.InnerText.ToLower(); + if (!userWordList.Contains(s)) + userWordList.Add(s); + } + } + return userWordListXmlFileName; + } + + public static string LoadUserWordList(HashSet userWordList, string languageName) + { + userWordList.Clear(); + var userWordDictionary = new XmlDocument(); + string userWordListXmlFileName = DictionaryFolder + languageName + "_user.xml"; + if (File.Exists(userWordListXmlFileName)) + { + userWordDictionary.Load(userWordListXmlFileName); + foreach (XmlNode node in userWordDictionary.DocumentElement.SelectNodes("word")) + { + string s = node.InnerText.ToLower(); + if (!userWordList.Contains(s)) + userWordList.Add(s); + } + } + return userWordListXmlFileName; + } + + public static readonly string UppercaseLetters = GetLetters(true, false, false); + public static readonly string LowercaseLetters = GetLetters(false, true, false); + public static readonly string LowercaseLettersWithNumbers = GetLetters(false, true, true); + public static readonly string AllLetters = GetLetters(true, true, false); + public static readonly string AllLettersAndNumbers = GetLetters(true, true, true); + + private static string GetLetters(bool uppercase, bool lowercase, bool numbers) + { + var sb = new StringBuilder(); + + if (uppercase) + sb.Append(Configuration.Settings.General.UppercaseLetters.ToUpper()); + + if (lowercase) + sb.Append(Configuration.Settings.General.UppercaseLetters.ToLower()); + + if (numbers) + sb.Append("0123456789"); + + return sb.ToString(); + } + + public static Color GetColorFromUserName(string userName) + { + if (string.IsNullOrEmpty(userName)) + return Color.Pink; + + byte[] buffer = Encoding.UTF8.GetBytes(userName); + long number = 0; + foreach (byte b in buffer) + number += b; + + switch (number % 20) + { + case 0: return Color.Red; + case 1: return Color.Blue; + case 2: return Color.Green; + case 3: return Color.DarkCyan; + case 4: return Color.DarkGreen; + case 5: return Color.DarkBlue; + case 6: return Color.DarkTurquoise; + case 7: return Color.DarkViolet; + case 8: return Color.DeepPink; + case 9: return Color.DodgerBlue; + case 10: return Color.ForestGreen; + case 11: return Color.Fuchsia; + case 12: return Color.DarkOrange; + case 13: return Color.GreenYellow; + case 14: return Color.IndianRed; + case 15: return Color.Indigo; + case 16: return Color.LawnGreen; + case 17: return Color.LightBlue; + case 18: return Color.DarkGoldenrod; + case 19: return Color.Magenta; + default: + return Color.Black; + } + } + + public static int GetNumber0To7FromUserName(string userName) + { + if (string.IsNullOrEmpty(userName)) + return 0; + + byte[] buffer = Encoding.UTF8.GetBytes(userName); + long number = 0; + foreach (byte b in buffer) + number += b; + + return (int)(number % 8); + } + + public static string GetRegExGroup(string regEx) + { + var start = regEx.IndexOf("(?<", StringComparison.Ordinal); + if (start < 0) + return null; + start += 3; + var end = regEx.IndexOf('>', start); + if (end <= start) + return null; + return regEx.Substring(start, end - start); + } + + public static string LowercaseVowels + { + get + { + return "aeiouyæøåéóáôèòæøåäöïɤəɛʊʉɨ"; + } + } + + public static void SetButtonHeight(Control control, int newHeight, int level) + { + if (level > 6) + return; + + if (control.HasChildren) + { + foreach (Control subControl in control.Controls) + { + if (subControl.HasChildren) + SetButtonHeight(subControl, newHeight, level + 1); + else if (subControl is Button) + subControl.Height = newHeight; + } + } + else if (control is Button) + control.Height = newHeight; + } + + public static int CountTagInText(string text, string tag) + { + int count = 0; + int index = text.IndexOf(tag, StringComparison.Ordinal); + while (index >= 0) + { + count++; + index = index + tag.Length; + if (index >= text.Length) + return count; + index = text.IndexOf(tag, index, StringComparison.Ordinal); + } + return count; + } + + public static int CountTagInText(string text, char tag) + { + int count = 0; + int index = text.IndexOf(tag); + while (index >= 0) + { + count++; + if ((index + 1) == text.Length) + return count; + index = text.IndexOf(tag, index + 1); + } + return count; + } + + public static bool StartsAndEndsWithTag(string text, string startTag, string endTag) + { + if (string.IsNullOrWhiteSpace(text)) + return false; + if (!text.Contains(startTag) || !text.Contains(endTag)) + return false; + + while (text.Contains(" ")) + text = text.Replace(" ", " "); + + var s1 = "- " + startTag; + var s2 = "-" + startTag; + var s3 = "- ..." + startTag; + var s4 = "- " + startTag + "..."; // - ... + + var e1 = endTag + "."; + var e2 = endTag + "!"; + var e3 = endTag + "?"; + var e4 = endTag + "..."; + var e5 = endTag + "-"; + + bool isStart = false; + bool isEnd = false; + if (text.StartsWith(startTag, StringComparison.Ordinal) || text.StartsWith(s1, StringComparison.Ordinal) || text.StartsWith(s2, StringComparison.Ordinal) || text.StartsWith(s3, StringComparison.Ordinal) || text.StartsWith(s4, StringComparison.Ordinal)) + isStart = true; + if (text.EndsWith(endTag, StringComparison.Ordinal) || text.EndsWith(e1, StringComparison.Ordinal) || text.EndsWith(e2, StringComparison.Ordinal) || text.EndsWith(e3, StringComparison.Ordinal) || text.EndsWith(e4, StringComparison.Ordinal) || text.EndsWith(e5, StringComparison.Ordinal)) + isEnd = true; + return isStart && isEnd; + } + + public static Paragraph GetOriginalParagraph(int index, Paragraph paragraph, List originalParagraphs) + { + if (index < 0) + return null; + + if (index < originalParagraphs.Count && Math.Abs(originalParagraphs[index].StartTime.TotalMilliseconds - paragraph.StartTime.TotalMilliseconds) < 50) + return originalParagraphs[index]; + + foreach (Paragraph p in originalParagraphs) + { + if (p.StartTime.TotalMilliseconds == paragraph.StartTime.TotalMilliseconds) + return p; + } + + foreach (Paragraph p in originalParagraphs) + { + if (p.StartTime.TotalMilliseconds > paragraph.StartTime.TotalMilliseconds - 200 && + p.StartTime.TotalMilliseconds < paragraph.StartTime.TotalMilliseconds + 1000) + return p; + } + return null; + } + + /// + /// UrlEncodes a string without the requirement for System.Web + /// + public static string UrlEncode(string text) + { + return Uri.EscapeDataString(text); + } + + /// + /// UrlDecodes a string without requiring System.Web + /// + public static string UrlDecode(string text) + { + // pre-process for + sign space formatting since System.Uri doesn't handle it + // plus literals are encoded as %2b normally so this should be safe + text = text.Replace('+', ' '); + return Uri.UnescapeDataString(text); + } + + public static void CheckAutoWrap(TextBox textBox, KeyEventArgs e, int numberOfNewLines) + { + int length = HtmlUtil.RemoveHtmlTags(textBox.Text).Length; + if (e.Modifiers == Keys.None && e.KeyCode != Keys.Enter && numberOfNewLines < 1 && length > Configuration.Settings.General.SubtitleLineMaximumLength) + { + if (Configuration.Settings.General.AutoWrapLineWhileTyping) // only if auto-break-setting is true + { + string newText; + if (length > Configuration.Settings.General.SubtitleLineMaximumLength + 30) + { + newText = AutoBreakLine(textBox.Text); + } + else + { + int lastSpace = textBox.Text.LastIndexOf(' '); + if (lastSpace > 0) + newText = textBox.Text.Remove(lastSpace, 1).Insert(lastSpace, Environment.NewLine); + else + newText = textBox.Text; + } + + int autobreakIndex = newText.IndexOf(Environment.NewLine, StringComparison.Ordinal); + if (autobreakIndex > 0) + { + int selectionStart = textBox.SelectionStart; + textBox.Text = newText; + if (selectionStart > autobreakIndex) + selectionStart += Environment.NewLine.Length - 1; + if (selectionStart >= 0) + textBox.SelectionStart = selectionStart; + } + } + } + } + + private static readonly Dictionary AllKeys = new Dictionary(); + public static Keys GetKeys(string keysInString) + { + if (string.IsNullOrEmpty(keysInString)) + return Keys.None; + + if (AllKeys.Count == 0) + { + foreach (Keys val in Enum.GetValues(typeof(Keys))) + { + string k = val.ToString().ToLower(); + if (!AllKeys.ContainsKey(k)) + AllKeys.Add(k, val); + } + if (!AllKeys.ContainsKey("pagedown")) + AllKeys.Add("pagedown", Keys.RButton | Keys.Space); + if (!AllKeys.ContainsKey("home")) + AllKeys.Add("home", Keys.MButton | Keys.Space); + if (!AllKeys.ContainsKey("capslock")) + AllKeys.Add("capslock", Keys.CapsLock); + } + + string[] parts = keysInString.ToLower().Split(new[] { '+' }, StringSplitOptions.RemoveEmptyEntries); + var resultKeys = Keys.None; + foreach (string k in parts) + { + if (AllKeys.ContainsKey(k)) + resultKeys = resultKeys | AllKeys[k]; + } + return resultKeys; + } + + public static string FixEnglishTextInRightToLeftLanguage(string text, string reverseChars) + { + var sb = new StringBuilder(); + var lines = text.SplitToLines(); + foreach (string line in lines) + { + string s = line.Trim(); + for (int i = 0; i < s.Length; i++) + { + if (s[i] == ')') + s = s.Remove(i, 1).Insert(i, "("); + else if (s[i] == '(') + s = s.Remove(i, 1).Insert(i, ")"); + } + + bool numbersOn = false; + string numbers = string.Empty; + for (int i = 0; i < s.Length; i++) + { + if (numbersOn && reverseChars.Contains(s[i])) + { + numbers = s[i] + numbers; + } + else if (numbersOn) + { + numbersOn = false; + s = s.Remove(i - numbers.Length, numbers.Length).Insert(i - numbers.Length, numbers); + numbers = string.Empty; + } + else if (reverseChars.Contains(s[i])) + { + numbers = s[i] + numbers; + numbersOn = true; + } + } + if (numbersOn) + { + int i = s.Length; + s = s.Remove(i - numbers.Length, numbers.Length).Insert(i - numbers.Length, numbers); + } + + sb.AppendLine(s); + } + return sb.ToString().Trim(); + } + + public static string ToSuperscript(string text) + { + var sb = new StringBuilder(); + var superscript = new List{ + '⁰', + '¹', + '²', + '³', + '⁴', + '⁵', + '⁶', + '⁷', + '⁸', + '⁹', + '⁺', + '⁻', + '⁼', + '⁽', + '⁾', + 'ᵃ', + 'ᵇ', + 'ᶜ', + 'ᵈ', + 'ᵉ', + 'ᶠ', + 'ᵍ', + 'ʰ', + 'ⁱ', + 'ʲ', + 'ᵏ', + 'ˡ', + 'ᵐ', + 'ⁿ', + 'ᵒ', + 'ᵖ', + 'ʳ', + 'ˢ', + 'ᵗ', + 'ᵘ', + 'ᵛ', + 'ʷ', + 'ˣ', + 'ʸ', + 'ᶻ', + 'ᴬ', + 'ᴮ', + 'ᴰ', + 'ᴱ', + 'ᴳ', + 'ᴴ', + 'ᴵ', + 'ᴶ', + 'ᴷ', + 'ᴸ', + 'ᴹ', + 'ᴺ', + 'ᴼ', + 'ᴾ', + 'ᴿ', + 'ᵀ', + 'ᵁ', + 'ᵂ' + }; + var normal = new List{ + '0', // "⁰" + '1', // "¹" + '2', // "²" + '3', // "³" + '4', // "⁴" + '5', // "⁵" + '6', // "⁶" + '7', // "⁷" + '8', // "⁸" + '9', // "⁹" + '+', // "⁺" + '-', // "⁻" + '=', // "⁼" + '(', // "⁽" + ')', // "⁾" + 'a', // "ᵃ" + 'b', // "ᵇ" + 'c', // "ᶜ" + 'd', // "ᵈ" + 'e', // "ᵉ" + 'f', // "ᶠ" + 'g', // "ᵍ" + 'h', // "ʰ" + 'i', // "ⁱ" + 'j', // "ʲ" + 'k', // "ᵏ" + 'l', // "ˡ" + 'm', // "ᵐ" + 'n', // "ⁿ" + 'o', // "ᵒ" + 'p', // "ᵖ" + 'r', // "ʳ" + 's', // "ˢ" + 't', // "ᵗ" + 'u', // "ᵘ" + 'v', // "ᵛ" + 'w', // "ʷ" + 'x', // "ˣ" + 'y', // "ʸ" + 'z', // "ᶻ" + 'A', // "ᴬ" + 'B', // "ᴮ" + 'D', // "ᴰ" + 'E', // "ᴱ" + 'G', // "ᴳ" + 'H', // "ᴴ" + 'I', // "ᴵ" + 'J', // "ᴶ" + 'K', // "ᴷ" + 'L', // "ᴸ" + 'M', // "ᴹ" + 'N', // "ᴺ" + 'O', // "ᴼ" + 'P', // "ᴾ" + 'R', // "ᴿ" + 'T', // "ᵀ" + 'U', // "ᵁ" + 'W', // "ᵂ" + }; + for (int i = 0; i < text.Length; i++) + { + char s = text[i]; + int index = normal.IndexOf(s); + if (index >= 0) + sb.Append(superscript[index]); + else + sb.Append(s); + } + return sb.ToString(); + } + + public static string ToSubscript(string text) + { + var sb = new StringBuilder(); + var subcript = new List{ + '₀', + '₁', + '₂', + '₃', + '₄', + '₅', + '₆', + '₇', + '₈', + '₉', + '₊', + '₋', + '₌', + '₍', + '₎', + 'ₐ', + 'ₑ', + 'ᵢ', + 'ₒ', + 'ᵣ', + 'ᵤ', + 'ᵥ', + 'ₓ', + }; + var normal = new List + { + '0', // "₀" + '1', // "₁" + '2', // "₂" + '3', // "₃" + '4', // "₄" + '5', // "₅" + '6', // "₆" + '7', // "₇" + '8', // "₈" + '9', // "₉" + '+', // "₊" + '-', // "₋" + '=', // "₌" + '(', // "₍" + ')', // "₎" + 'a', // "ₐ" + 'e', // "ₑ" + 'i', // "ᵢ" + 'o', // "ₒ" + 'r', // "ᵣ" + 'u', // "ᵤ" + 'v', // "ᵥ" + 'x', // "ₓ" + }; + for (int i = 0; i < text.Length; i++) + { + char s = text[i]; + int index = normal.IndexOf(s); + if (index >= 0) + sb.Append(subcript[index]); + else + sb.Append(s); + } + return sb.ToString(); + } + + public static string FixQuotes(string text) + { + if (string.IsNullOrEmpty(text)) + return text; + + if (text.StartsWith('"') && text.Length > 1) + text = text.Substring(1); + + if (text.EndsWith('"') && text.Length >= 1) + text = text.Substring(0, text.Length - 1); + + return text.Replace("\"\"", "\""); + } + + public static Color GetColorFromFontString(string text, Color defaultColor) + { + string s = text.TrimEnd(); + int start = s.IndexOf("= 0 && s.EndsWith("", StringComparison.OrdinalIgnoreCase)) + { + int end = s.IndexOf('>', start); + if (end > 0) + { + string f = s.Substring(start, end - start); + if (f.Contains(" color=")) + { + int colorStart = f.IndexOf(" color=", StringComparison.OrdinalIgnoreCase); + if (s.IndexOf('"', colorStart + " color=".Length + 1) > 0) + end = s.IndexOf('"', colorStart + " color=".Length + 1); + s = s.Substring(colorStart, end - colorStart); + s = s.Replace(" color=", string.Empty); + s = s.Trim('\'').Trim('"').Trim('\''); + try + { + if (s.StartsWith("rgb(", StringComparison.Ordinal)) + { + var arr = s.Remove(0, 4).TrimEnd(')').Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + return Color.FromArgb(int.Parse(arr[0]), int.Parse(arr[1]), int.Parse(arr[2])); + } + return ColorTranslator.FromHtml(s); + } + catch + { + return defaultColor; + } + } + } + } + return defaultColor; + } + + public static string[] SplitForChangedCalc(string s, bool ignoreLineBreaks, bool breakToLetters) + { + const string endChars = "!?.:;,#%$£"; + var list = new List(); + + if (breakToLetters) + { + foreach (char ch in s) + list.Add(ch.ToString()); + } + else + { + var word = new StringBuilder(); + int i = 0; + while (i < s.Length) + { + if (ignoreLineBreaks && s.Substring(i).StartsWith(Environment.NewLine, StringComparison.Ordinal)) + { + if (word.Length > 0) + list.Add(word.ToString()); + word.Clear(); + i += Environment.NewLine.Length; + } + else if (s.Substring(i).StartsWith(Environment.NewLine, StringComparison.Ordinal)) + { + if (word.Length > 0) + list.Add(word.ToString()); + word.Clear(); + list.Add(Environment.NewLine); + i += Environment.NewLine.Length; + } + else if (s[i] == ' ') + { + if (word.Length > 0) + list.Add(word.ToString()); + word.Clear(); + i++; + } + else if (endChars.Contains(s[i]) && (word.Length == 0 || endChars.Contains(word[0]))) + { + word.Append(s[i]); + i++; + } + else if (endChars.Contains(s[i])) + { + if (word.Length > 0) + list.Add(word.ToString()); + word.Clear(); + word.Append(s[i]); + i++; + } + else + { + word.Append(s[i]); + i++; + } + } + if (word.Length > 0) + list.Add(word.ToString()); + } + return list.ToArray(); + } + + public static void GetTotalAndChangedWords(string s1, string s2, ref int total, ref int change, bool ignoreLineBreaks, bool breakToLetters) + { + var parts1 = SplitForChangedCalc(s1, ignoreLineBreaks, breakToLetters); + var parts2 = SplitForChangedCalc(s2, ignoreLineBreaks, breakToLetters); + int t = Math.Max(parts1.Length, parts2.Length); + total += t; + int c = GetChangesAdvanced(parts1, parts2); + change += c; + } + + private static int GetChangesAdvanced(string[] parts1, string[] parts2) + { + int i1 = 0; + int i2 = 0; + int i = 0; + int c = 0; + while (i < Math.Max(parts1.Length, parts2.Length) && i1 < parts1.Length && i2 < parts2.Length) + { + if (parts1[i1] == parts2[i2]) + { + i1++; + i2++; + } + else + { + int i1Next = FindNext(parts2[i2], parts1, i1); + int i2Next = FindNext(parts1[i1], parts2, i2); + if (i1Next < i2Next) + { + c += i1Next - i1; + i1 = i1Next + 1; + i2++; + } + else if (i2Next < i1Next) + { + c += i2Next - i2; + i1++; + i2 = i2Next + 1; + } + else + { + i1++; + i2++; + c++; + } + } + i++; + } + if (i1 == parts1.Length && i2 == parts2.Length) + return c; + + return c + Math.Abs(parts1.Length - parts2.Length); + } + + private static int FindNext(string s, string[] parts, int startIndex) + { + for (int i = startIndex; i < parts.Length; i++) + { + if (s == parts[i]) + return i; + } + return int.MaxValue; + } + + public static string RemoveNonNumbers(string p) + { + if (string.IsNullOrEmpty(p)) + return p; + + var sb = new StringBuilder(); + foreach (var c in p) + { + if (char.IsDigit(c)) + sb.Append(c); + } + return sb.ToString(); + } + + /// + /// Remove unneeded spaces + /// + /// text string to remove unneeded spaces from + /// two letter language id string + /// text with unneeded spaces removed + public static string RemoveUnneededSpaces(string text, string language) + { + const string zeroWidthSpace = "\u200B"; + const string zeroWidthNoBreakSpace = "\uFEFF"; + const string noBreakSpace = "\u00A0"; + const string operatingSystemCommand = "\u009D"; + + text = text.Trim(); + + text = text.Replace(zeroWidthSpace, string.Empty); + text = text.Replace(zeroWidthNoBreakSpace, string.Empty); + text = text.Replace(noBreakSpace, " "); + text = text.Replace(operatingSystemCommand, string.Empty); + text = text.Replace('\t', ' '); + + text = text.FixExtraSpaces(); + + if (text.EndsWith(' ')) + text = text.Substring(0, text.Length - 1); + + const string ellipses = "..."; + text = text.Replace(". . ..", ellipses); + text = text.Replace(". ...", ellipses); + text = text.Replace(". .. .", ellipses); + text = text.Replace(". . .", ellipses); + text = text.Replace(". ..", ellipses); + text = text.Replace(".. .", ellipses); + + // Fix recursive: ... + while (text.Contains("....")) + text = text.Replace("....", ellipses); + + text = text.Replace(" ..." + Environment.NewLine, "..." + Environment.NewLine); + text = text.Replace(Environment.NewLine + "... ", Environment.NewLine + "..."); + text = text.Replace(Environment.NewLine + "... ", Environment.NewLine + "..."); + text = text.Replace(Environment.NewLine + "- ... ", Environment.NewLine + "- ..."); + text = text.Replace(Environment.NewLine + "- ... ", Environment.NewLine + "- ..."); + text = text.Replace(Environment.NewLine + "- ... ", Environment.NewLine + "- ..."); + + if (text.StartsWith("... ", StringComparison.Ordinal)) + text = text.Remove(3, 1); + if (text.EndsWith(" ...", StringComparison.Ordinal)) + text = text.Remove(text.Length - 4, 1); + if (text.EndsWith(" ...", StringComparison.Ordinal)) + text = text.Remove(text.Length - 8, 1); + if (text.StartsWith("- ... ", StringComparison.Ordinal)) + text = text.Remove(5, 1); + if (text.StartsWith("... ", StringComparison.Ordinal)) + text = text.Remove(6, 1); + + if (language != "fr") // special rules for French + { + text = text.Replace("... ?", "...?"); + text = text.Replace("... !", "...!"); + + text = text.Replace(" :", ":"); + text = text.Replace(" :", ":"); + } + + if (!text.Contains("- ...")) + text = text.Replace(" ... ", "... "); + + while (text.Contains(" ,")) + text = text.Replace(" ,", ","); + + if (text.EndsWith(" .", StringComparison.Ordinal)) + text = text.Remove(text.Length - 2, 1); + + if (text.EndsWith(" \"", StringComparison.Ordinal)) + text = text.Remove(text.Length - 2, 1); + + if (text.Contains(" \"" + Environment.NewLine)) + text = text.Replace(" \"" + Environment.NewLine, "\"" + Environment.NewLine); + + if (text.Contains(" ." + Environment.NewLine)) + text = text.Replace(" ." + Environment.NewLine, "." + Environment.NewLine); + + if (language != "fr") // special rules for French + { + if (text.Contains(" !")) + text = text.Replace(" !", "!"); + + if (text.Contains(" ?")) + text = text.Replace(" ?", "?"); + } + + while (text.Contains("¿ ")) + text = text.Replace("¿ ", "¿"); + + while (text.Contains("¡ ")) + text = text.Replace("¡ ", "¡"); + + // Italic + if (text.Contains("", StringComparison.OrdinalIgnoreCase) && text.Contains("", StringComparison.OrdinalIgnoreCase)) + text = RemoveSpaceBeforeAfterTag(text, ""); + + // Bold + if (text.Contains("", StringComparison.OrdinalIgnoreCase) && text.Contains("", StringComparison.OrdinalIgnoreCase)) + text = RemoveSpaceBeforeAfterTag(text, ""); + + // Underline + if (text.Contains("", StringComparison.OrdinalIgnoreCase) && text.Contains("", StringComparison.OrdinalIgnoreCase)) + text = RemoveSpaceBeforeAfterTag(text, ""); + + // Font + if (text.Contains("', idx + 6); + if (endIdx > idx && endIdx < text.Length - 8) + { + var color = text.Substring(idx, (endIdx - idx) + 1).ToLower(); + text = RemoveSpaceBeforeAfterTag(text, color); + } + } + text = text.Trim(); + text = text.Replace(Environment.NewLine + " ", Environment.NewLine); + + if (text.Contains("- ") && text.Length > 5) + { + int idx = text.IndexOf("- ", 2, StringComparison.Ordinal); + if (text.StartsWith("", StringComparison.OrdinalIgnoreCase)) + idx = text.IndexOf("- ", 5, StringComparison.Ordinal); + while (idx > 0) + { + if (idx > 0 && idx < text.Length - 2) + { + string before = string.Empty; + int k = idx - 1; + while (k >= 0 && AllLettersAndNumbers.Contains(text[k])) + { + before = text[k] + before; + k--; + } + string after = string.Empty; + k = idx + 2; + while (k < text.Length && AllLetters.Contains(text[k])) + { + after = after + text[k]; + k++; + } + if (after.Length > 0 && after.Equals(before, StringComparison.OrdinalIgnoreCase)) + text = text.Remove(idx + 1, 1); + else if (before.Length > 0) + { + if ((language == "en" && (after.Equals("and", StringComparison.OrdinalIgnoreCase) || after.Equals("or", StringComparison.OrdinalIgnoreCase))) || + (language == "es" && (after.Equals("y", StringComparison.OrdinalIgnoreCase) || after.Equals("o", StringComparison.OrdinalIgnoreCase))) || + (language == "da" && (after.Equals("og", StringComparison.OrdinalIgnoreCase) || after.Equals("eller", StringComparison.OrdinalIgnoreCase))) || + (language == "de" && (after.Equals("und", StringComparison.OrdinalIgnoreCase) || after.Equals("oder", StringComparison.OrdinalIgnoreCase))) || + (language == "fi" && (after.Equals("ja", StringComparison.OrdinalIgnoreCase) || after.Equals("tai", StringComparison.OrdinalIgnoreCase))) || + (language == "fr" && (after.Equals("et", StringComparison.OrdinalIgnoreCase) || after.Equals("ou", StringComparison.OrdinalIgnoreCase))) || + (language == "it" && (after.Equals("e", StringComparison.OrdinalIgnoreCase) || after.Equals("o", StringComparison.OrdinalIgnoreCase))) || + (language == "nl" && (after.Equals("en", StringComparison.OrdinalIgnoreCase) || after.Equals("of", StringComparison.OrdinalIgnoreCase))) || + (language == "pl" && (after.Equals("i", StringComparison.OrdinalIgnoreCase) || after.Equals("czy", StringComparison.OrdinalIgnoreCase))) || + (language == "pt" && (after.Equals("e", StringComparison.OrdinalIgnoreCase) || after.Equals("ou", StringComparison.OrdinalIgnoreCase)))) + { + } + else + { + text = text.Remove(idx + 1, 1); + } + } + } + if (idx + 1 < text.Length && idx != -1) + idx = text.IndexOf("- ", idx + 1, StringComparison.Ordinal); + else + break; + } + } + + if (CountTagInText(text, '"') == 2 && text.Contains(" \" ")) + { + int idx = text.IndexOf(" \" ", StringComparison.Ordinal); + int idxp = text.IndexOf('"'); + + //"Foo " bar. + if ((idxp >= 0 && idxp < idx) && AllLettersAndNumbers.Contains(text[idx - 1]) && !" \r\n".Contains(text[idxp + 1])) + { + text = text.Remove(idx, 1); + } + + //" Foo " bar. + idx = text.IndexOf(" \" ", StringComparison.Ordinal); + idxp = text.IndexOf('"'); + if (idxp >= 0 && idx > idxp) + { + if (text[idxp + 1] == ' ' && AllLettersAndNumbers.Contains(text[idxp + 2])) + { + text = text.Remove(idxp + 1, 1); + idx--; + } + text = text.Remove(idx, 1); + } + } + return text; + } + + public static string RemoveSpaceBeforeAfterTag(string text, string openTag) + { + text = HtmlUtil.FixUpperTags(text); + var closeTag = string.Empty; + switch (openTag) + { + case "": + closeTag = ""; + break; + case "": + closeTag = ""; + break; + case "": + closeTag = ""; + break; + } + + if (closeTag.Length == 0 && openTag.Contains("
Foobar + if (text.StartsWith(open1, StringComparison.Ordinal)) + text = openTag + text.Substring(open1.Length); + + if (text.Contains(open2, StringComparison.Ordinal)) + text = text.Replace(open2, Environment.NewLine + openTag); + + // Hi bad man! -> Hi bad man! + text = text.Replace(" " + openTag + " ", " " + openTag); + text = text.Replace(Environment.NewLine + openTag + " ", Environment.NewLine + openTag); + + // Hi bad man! -> Hi bad man! + text = text.Replace(" " + closeTag + " ", closeTag + " "); + text = text.Replace(" " + closeTag + Environment.NewLine, closeTag + Environment.NewLine); + + text = text.Trim(); + if (text.StartsWith(open1, StringComparison.Ordinal)) + text = openTag + text.Substring(open1.Length); + + return text; + } + + /// + /// Creates a task that will complete after a time delay. + /// + /// The number of milliseconds to wait before completing the returned task. + /// A task that represents the time delay. + public static Task TaskDelay(int millisecondsDelay) + { + var tcs = new TaskCompletionSource(); + var t = new System.Threading.Timer(_ => tcs.SetResult(null)); + t.Change(millisecondsDelay, -1); + return tcs.Task; + } + + public static SubtitleFormat LoadMatroskaTextSubtitle(MatroskaTrackInfo matroskaSubtitleInfo, MatroskaFile matroska, List sub, Subtitle subtitle) + { + if (subtitle == null) + throw new ArgumentNullException("subtitle"); + subtitle.Paragraphs.Clear(); + + var isSsa = false; + SubtitleFormat format = new SubRip(); + if (matroskaSubtitleInfo.CodecPrivate.Contains("[script info]", StringComparison.OrdinalIgnoreCase)) + { + if (matroskaSubtitleInfo.CodecPrivate.Contains("[V4 Styles]", StringComparison.OrdinalIgnoreCase)) + format = new SubStationAlpha(); + else + format = new AdvancedSubStationAlpha(); + isSsa = true; + } + + if (isSsa) + { + foreach (var p in LoadMatroskaSSA(matroskaSubtitleInfo, matroska.Path, format, sub).Paragraphs) + { + subtitle.Paragraphs.Add(p); + } + + if (!string.IsNullOrEmpty(matroskaSubtitleInfo.CodecPrivate)) + { + bool eventsStarted = false; + bool fontsStarted = false; + bool graphicsStarted = false; + var header = new StringBuilder(); + foreach (string line in matroskaSubtitleInfo.CodecPrivate.Replace(Environment.NewLine, "\n").Split('\n')) + { + if (!eventsStarted && !fontsStarted && !graphicsStarted) + { + header.AppendLine(line); + } + + if (line.TrimStart().StartsWith("dialog:", StringComparison.OrdinalIgnoreCase)) + { + eventsStarted = true; + fontsStarted = false; + graphicsStarted = false; + } + else if (line.Trim().Equals("[events]", StringComparison.OrdinalIgnoreCase)) + { + eventsStarted = true; + fontsStarted = false; + graphicsStarted = false; + } + else if (line.Trim().Equals("[fonts]", StringComparison.OrdinalIgnoreCase)) + { + eventsStarted = false; + fontsStarted = true; + graphicsStarted = false; + } + else if (line.Trim().Equals("[graphics]", StringComparison.OrdinalIgnoreCase)) + { + eventsStarted = false; + fontsStarted = false; + graphicsStarted = true; + } + } + subtitle.Header = header.ToString().TrimEnd(); + if (!subtitle.Header.Contains("[events]", StringComparison.OrdinalIgnoreCase)) + { + subtitle.Header += Environment.NewLine + Environment.NewLine + "[Events]" + Environment.NewLine; + } + } + } + else + { + foreach (var p in sub) + { + subtitle.Paragraphs.Add(new Paragraph(p.Text, p.Start, p.End)); + } + } + subtitle.Renumber(); + return format; + } + + public static Subtitle LoadMatroskaSSA(MatroskaTrackInfo matroskaSubtitleInfo, string fileName, SubtitleFormat format, List sub) + { + var subtitle = new Subtitle { Header = matroskaSubtitleInfo.CodecPrivate }; + var lines = new List(); + foreach (string l in subtitle.Header.Trim().SplitToLines()) + lines.Add(l); + var footer = new StringBuilder(); + var comments = new Subtitle(); + if (!string.IsNullOrEmpty(matroskaSubtitleInfo.CodecPrivate)) + { + bool footerOn = false; + foreach (string line in lines) + { + if (footerOn) + { + footer.AppendLine(line); + } + else if (line.Trim() == "[Events]") + { + } + else if (line.Trim() == "[Fonts]" || line.Trim() == "[Graphics]") + { + footerOn = true; + footer.AppendLine(); + footer.AppendLine(); + footer.AppendLine(line); + } + else if (line.StartsWith("Comment:", StringComparison.Ordinal)) + { + var arr = line.Split(','); + if (arr.Length > 3) + { + arr = arr[1].Split(new[] { ':', '.' }); + if (arr.Length == 4) + { + int hour; + int min; + int sec; + int ms; + if (int.TryParse(arr[0], out hour) && int.TryParse(arr[1], out min) && + int.TryParse(arr[2], out sec) && int.TryParse(arr[3], out ms)) + { + comments.Paragraphs.Add(new Paragraph(new TimeCode(hour, min, sec, ms * 10), new TimeCode(0, 0, 0, 0), line)); + } + } + } + } + } + } + const string headerFormat = "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text"; + if (!subtitle.Header.Contains("[Events]")) + { + subtitle.Header = subtitle.Header.Trim() + Environment.NewLine + + Environment.NewLine + + "[Events]" + Environment.NewLine + + headerFormat + Environment.NewLine; + } + else + { + subtitle.Header = subtitle.Header.Remove(subtitle.Header.IndexOf("[Events]", StringComparison.Ordinal)); + subtitle.Header = subtitle.Header.Trim() + Environment.NewLine + + Environment.NewLine + + "[Events]" + Environment.NewLine + + headerFormat + Environment.NewLine; + } + lines = new List(); + foreach (string l in subtitle.Header.Trim().SplitToLines()) + lines.Add(l); + + const string timeCodeFormat = "{0}:{1:00}:{2:00}.{3:00}"; // h:mm:ss.cc + foreach (var mp in sub) + { + var p = new Paragraph(string.Empty, mp.Start, mp.End); + string start = string.Format(timeCodeFormat, p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, p.StartTime.Milliseconds / 10); + string end = string.Format(timeCodeFormat, p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, p.EndTime.Milliseconds / 10); + + //MKS contains this: ReadOrder, Layer, Style, Name, MarginL, MarginR, MarginV, Effect, Text + + for (int commentIndex = 0; commentIndex < comments.Paragraphs.Count; commentIndex++) + { + var cp = comments.Paragraphs[commentIndex]; + if (cp.StartTime.TotalMilliseconds <= p.StartTime.TotalMilliseconds) + lines.Add(cp.Text); + } + for (int commentIndex = comments.Paragraphs.Count - 1; commentIndex >= 0; commentIndex--) + { + var cp = comments.Paragraphs[commentIndex]; + if (cp.StartTime.TotalMilliseconds <= p.StartTime.TotalMilliseconds) + comments.Paragraphs.RemoveAt(commentIndex); + } + + string text = mp.Text.Replace(Environment.NewLine, "\\N"); + int idx = text.IndexOf(',') + 1; + if (idx > 0 && idx < text.Length) + { + text = text.Remove(0, idx); // remove ReadOrder + idx = text.IndexOf(','); + text = text.Insert(idx, "," + start + "," + end); + lines.Add("Dialogue: " + text); + } + } + for (int commentIndex = 0; commentIndex < comments.Paragraphs.Count; commentIndex++) + { + var cp = comments.Paragraphs[commentIndex]; + lines.Add(cp.Text); + } + + foreach (string l in footer.ToString().SplitToLines()) + lines.Add(l); + + format.LoadSubtitle(subtitle, lines, fileName); + return subtitle; + } + + public static int GetNumberOfLines(string text) + { + if (string.IsNullOrEmpty(text)) + return 0; + + int lines = 1; + int idx = text.IndexOf('\n'); + while (idx != -1) + { + lines++; + idx = text.IndexOf('\n', idx + 1); + } + return lines; + } + + public static void FixLargeFonts(Control mainCtrl, Control ctrl) + { + using (Graphics graphics = mainCtrl.CreateGraphics()) + { + SizeF textSize = graphics.MeasureString(ctrl.Text, mainCtrl.Font); + if (textSize.Height > ctrl.Height - 4) + { + int newButtonHeight = (int)(textSize.Height + 7.5); + SetButtonHeight(mainCtrl, newButtonHeight, 1); + } + } + } + + } +} \ No newline at end of file diff --git a/libse/VideoInfo.cs b/libse/VideoInfo.cs new file mode 100644 index 000000000..c934d7b6b --- /dev/null +++ b/libse/VideoInfo.cs @@ -0,0 +1,15 @@ +namespace Nikse.SubtitleEdit.Core +{ + public class VideoInfo + { + public int Width { get; set; } + public int Height { get; set; } + public double TotalMilliseconds { get; set; } + public double TotalSeconds { get; set; } + public double FramesPerSecond { get; set; } + public double TotalFrames { get; set; } + public string VideoCodec { get; set; } + public string FileType { get; set; } + public bool Success { get; set; } + } +} \ No newline at end of file diff --git a/libse/VobSub/Helper.cs b/libse/VobSub/Helper.cs new file mode 100644 index 000000000..47fa619b5 --- /dev/null +++ b/libse/VobSub/Helper.cs @@ -0,0 +1,325 @@ +using System; +using System.Text; + +namespace Nikse.SubtitleEdit.Core.VobSub +{ + public static class Helper + { + + #region Binary constants + + public const int B00000000 = 0; + public const int B00000001 = 1; + public const int B00000010 = 2; + public const int B00000011 = 3; + public const int B00000100 = 4; + public const int B00000101 = 5; + public const int B00000110 = 6; + public const int B00000111 = 7; + public const int B00001000 = 8; + public const int B00001001 = 9; + public const int B00001010 = 10; + public const int B00001011 = 11; + public const int B00001100 = 12; + public const int B00001101 = 13; + public const int B00001110 = 14; + public const int B00001111 = 15; + public const int B00010000 = 16; + public const int B00010001 = 17; + public const int B00010010 = 18; + public const int B00010011 = 19; + public const int B00010100 = 20; + public const int B00010101 = 21; + public const int B00010110 = 22; + public const int B00010111 = 23; + public const int B00011000 = 24; + public const int B00011001 = 25; + public const int B00011010 = 26; + public const int B00011011 = 27; + public const int B00011100 = 28; + public const int B00011101 = 29; + public const int B00011110 = 30; + public const int B00011111 = 31; + public const int B00100000 = 32; + public const int B00100001 = 33; + public const int B00100010 = 34; + public const int B00100011 = 35; + public const int B00100100 = 36; + public const int B00100101 = 37; + public const int B00100110 = 38; + public const int B00100111 = 39; + public const int B00101000 = 40; + public const int B00101001 = 41; + public const int B00101010 = 42; + public const int B00101011 = 43; + public const int B00101100 = 44; + public const int B00101101 = 45; + public const int B00101110 = 46; + public const int B00101111 = 47; + public const int B00110000 = 48; + public const int B00110001 = 49; + public const int B00110010 = 50; + public const int B00110011 = 51; + public const int B00110100 = 52; + public const int B00110101 = 53; + public const int B00110110 = 54; + public const int B00110111 = 55; + public const int B00111000 = 56; + public const int B00111001 = 57; + public const int B00111010 = 58; + public const int B00111011 = 59; + public const int B00111100 = 60; + public const int B00111101 = 61; + public const int B00111110 = 62; + public const int B00111111 = 63; + public const int B01000000 = 64; + public const int B01000001 = 65; + public const int B01000010 = 66; + public const int B01000011 = 67; + public const int B01000100 = 68; + public const int B01000101 = 69; + public const int B01000110 = 70; + public const int B01000111 = 71; + public const int B01001000 = 72; + public const int B01001001 = 73; + public const int B01001010 = 74; + public const int B01001011 = 75; + public const int B01001100 = 76; + public const int B01001101 = 77; + public const int B01001110 = 78; + public const int B01001111 = 79; + public const int B01010000 = 80; + public const int B01010001 = 81; + public const int B01010010 = 82; + public const int B01010011 = 83; + public const int B01010100 = 84; + public const int B01010101 = 85; + public const int B01010110 = 86; + public const int B01010111 = 87; + public const int B01011000 = 88; + public const int B01011001 = 89; + public const int B01011010 = 90; + public const int B01011011 = 91; + public const int B01011100 = 92; + public const int B01011101 = 93; + public const int B01011110 = 94; + public const int B01011111 = 95; + public const int B01100000 = 96; + public const int B01100001 = 97; + public const int B01100010 = 98; + public const int B01100011 = 99; + public const int B01100100 = 100; + public const int B01100101 = 101; + public const int B01100110 = 102; + public const int B01100111 = 103; + public const int B01101000 = 104; + public const int B01101001 = 105; + public const int B01101010 = 106; + public const int B01101011 = 107; + public const int B01101100 = 108; + public const int B01101101 = 109; + public const int B01101110 = 110; + public const int B01101111 = 111; + public const int B01110000 = 112; + public const int B01110001 = 113; + public const int B01110010 = 114; + public const int B01110011 = 115; + public const int B01110100 = 116; + public const int B01110101 = 117; + public const int B01110110 = 118; + public const int B01110111 = 119; + public const int B01111000 = 120; + public const int B01111001 = 121; + public const int B01111010 = 122; + public const int B01111011 = 123; + public const int B01111100 = 124; + public const int B01111101 = 125; + public const int B01111110 = 126; + public const int B01111111 = 127; + public const int B10000000 = 128; + public const int B10000001 = 129; + public const int B10000010 = 130; + public const int B10000011 = 131; + public const int B10000100 = 132; + public const int B10000101 = 133; + public const int B10000110 = 134; + public const int B10000111 = 135; + public const int B10001000 = 136; + public const int B10001001 = 137; + public const int B10001010 = 138; + public const int B10001011 = 139; + public const int B10001100 = 140; + public const int B10001101 = 141; + public const int B10001110 = 142; + public const int B10001111 = 143; + public const int B10010000 = 144; + public const int B10010001 = 145; + public const int B10010010 = 146; + public const int B10010011 = 147; + public const int B10010100 = 148; + public const int B10010101 = 149; + public const int B10010110 = 150; + public const int B10010111 = 151; + public const int B10011000 = 152; + public const int B10011001 = 153; + public const int B10011010 = 154; + public const int B10011011 = 155; + public const int B10011100 = 156; + public const int B10011101 = 157; + public const int B10011110 = 158; + public const int B10011111 = 159; + public const int B10100000 = 160; + public const int B10100001 = 161; + public const int B10100010 = 162; + public const int B10100011 = 163; + public const int B10100100 = 164; + public const int B10100101 = 165; + public const int B10100110 = 166; + public const int B10100111 = 167; + public const int B10101000 = 168; + public const int B10101001 = 169; + public const int B10101010 = 170; + public const int B10101011 = 171; + public const int B10101100 = 172; + public const int B10101101 = 173; + public const int B10101110 = 174; + public const int B10101111 = 175; + public const int B10110000 = 176; + public const int B10110001 = 177; + public const int B10110010 = 178; + public const int B10110011 = 179; + public const int B10110100 = 180; + public const int B10110101 = 181; + public const int B10110110 = 182; + public const int B10110111 = 183; + public const int B10111000 = 184; + public const int B10111001 = 185; + public const int B10111010 = 186; + public const int B10111011 = 187; + public const int B10111100 = 188; + public const int B10111101 = 189; + public const int B10111110 = 190; + public const int B10111111 = 191; + public const int B11000000 = 192; + public const int B11000001 = 193; + public const int B11000010 = 194; + public const int B11000011 = 195; + public const int B11000100 = 196; + public const int B11000101 = 197; + public const int B11000110 = 198; + public const int B11000111 = 199; + public const int B11001000 = 200; + public const int B11001001 = 201; + public const int B11001010 = 202; + public const int B11001011 = 203; + public const int B11001100 = 204; + public const int B11001101 = 205; + public const int B11001110 = 206; + public const int B11001111 = 207; + public const int B11010000 = 208; + public const int B11010001 = 209; + public const int B11010010 = 210; + public const int B11010011 = 211; + public const int B11010100 = 212; + public const int B11010101 = 213; + public const int B11010110 = 214; + public const int B11010111 = 215; + public const int B11011000 = 216; + public const int B11011001 = 217; + public const int B11011010 = 218; + public const int B11011011 = 219; + public const int B11011100 = 220; + public const int B11011101 = 221; + public const int B11011110 = 222; + public const int B11011111 = 223; + public const int B11100000 = 224; + public const int B11100001 = 225; + public const int B11100010 = 226; + public const int B11100011 = 227; + public const int B11100100 = 228; + public const int B11100101 = 229; + public const int B11100110 = 230; + public const int B11100111 = 231; + public const int B11101000 = 232; + public const int B11101001 = 233; + public const int B11101010 = 234; + public const int B11101011 = 235; + public const int B11101100 = 236; + public const int B11101101 = 237; + public const int B11101110 = 238; + public const int B11101111 = 239; + public const int B11110000 = 240; + public const int B11110001 = 241; + public const int B11110010 = 242; + public const int B11110011 = 243; + public const int B11110100 = 244; + public const int B11110101 = 245; + public const int B11110110 = 246; + public const int B11110111 = 247; + public const int B11111000 = 248; + public const int B11111001 = 249; + public const int B11111010 = 250; + public const int B11111011 = 251; + public const int B11111100 = 252; + public const int B11111101 = 253; + public const int B11111110 = 254; + public const int B11111111 = 255; + + #endregion Binary constants + + public static string IntToHex(UInt64 value, int digits) + { + return value.ToString("X").PadLeft(digits, '0'); + } + + public static string IntToHex(int value, int digits) + { + return value.ToString("X").PadLeft(digits, '0'); + } + + public static string IntToBin(long value, int digits) + { + return Convert.ToString(value, 2).PadLeft(digits, '0'); + } + + public static UInt32 GetEndian(byte[] buffer, int index, int count) + { + UInt32 result = 0; + for (int i = 0; i < count; i++) + result = (result << 8) + buffer[index + i]; + return result; + } + + /// + /// Get two bytes word stored in endian order + /// + /// Byte array + /// Index in byte array + /// Word as int + public static int GetEndianWord(byte[] buffer, int index) + { + if (index + 1 < buffer.Length) + return (buffer[index] << 8) + buffer[index + 1]; + return 0; + } + + public static int GetLittleEndian32(byte[] buffer, int index) + { + return (buffer[index + 3] << 24 | (int)buffer[index + 2] << 16 | (int)buffer[index + 1] << 8 | (int)buffer[index + 0]); + } + + public static string GetBinaryString(byte[] buffer, int index, int count) + { + var sb = new StringBuilder(); + for (int i = 0; i < count; i++) + sb.Append(Convert.ToString(buffer[index + i], 2).PadLeft(8, '0')); + return sb.ToString(); + } + + public static UInt64 GetUInt32FromBinaryString(string binaryValue) + { + return Convert.ToUInt32(binaryValue, 2); + } + + } +} diff --git a/libse/VobSub/Idx.cs b/libse/VobSub/Idx.cs new file mode 100644 index 000000000..3db52c0cb --- /dev/null +++ b/libse/VobSub/Idx.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Globalization; +using System.IO; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.VobSub +{ + public class Idx + { + public readonly List IdxParagraphs = new List(); + public readonly List Palette = new List(); + public readonly List Languages = new List(); + + private static Regex timeCodeLinePattern = new Regex(@"^timestamp: \d+:\d+:\d+:\d+, filepos: [\dabcdefABCDEF]+$", RegexOptions.Compiled); + + public Idx(string fileName) + : this(File.ReadAllLines(fileName)) + { + } + + public Idx(string[] lines) + { + foreach (string line in lines) + { + if (timeCodeLinePattern.IsMatch(line)) + { + IdxParagraph p = GetTimeCodeAndFilePosition(line); + if (p != null) + IdxParagraphs.Add(p); + } + else if (line.StartsWith("palette:", StringComparison.OrdinalIgnoreCase) && line.Length > 10) + { + string s = line.Substring("palette:".Length + 1); + string[] colors = s.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries); + foreach (string hex in colors) + { + Palette.Add(HexToColor(hex)); + } + } + else if (line.StartsWith("id:", StringComparison.OrdinalIgnoreCase) && line.Length > 4) + { + //id: en, index: 1 + //id: es, index: 2 + string s = line.Substring("id:".Length + 1); + string[] parts = s.Split(new[] { ':', ',', ' ' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length > 0) + { + try + { + string twoLetterLanguageId = parts[0]; + CultureInfo info = CultureInfo.GetCultureInfoByIetfLanguageTag(twoLetterLanguageId); + Languages.Add(string.Format("{0} (0x{1:x})", info.NativeName, Languages.Count + 32)); + } + catch + { + } + } + } + } + } + + private static Color HexToColor(string hex) + { + hex = hex.TrimStart('#').Trim(); + if (hex.Length == 6) + { + int r = Convert.ToInt32(hex.Substring(0, 2), 16); + int g = Convert.ToInt32(hex.Substring(2, 2), 16); + int b = Convert.ToInt32(hex.Substring(4, 2), 16); + return Color.FromArgb(r, g, b); + } + else if (hex.Length == 8) + { + int a = Convert.ToInt32(hex.Substring(0, 2), 16); + int r = Convert.ToInt32(hex.Substring(2, 2), 16); + int g = Convert.ToInt32(hex.Substring(4, 2), 16); + int b = Convert.ToInt32(hex.Substring(6, 2), 16); + return Color.FromArgb(a, r, g, b); + } + return Color.Red; + } + + private static IdxParagraph GetTimeCodeAndFilePosition(string line) + { + // timestamp: 00:00:01:401, filepos: 000000000 + string[] parts = line.Split(new[] { ',', ':' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 7) + { + int hours; + int minutes; + int seconds; + int milliseconds; + if (int.TryParse(parts[1], out hours) && + int.TryParse(parts[2], out minutes) && + int.TryParse(parts[3], out seconds) && + int.TryParse(parts[4], out milliseconds)) + { + return new IdxParagraph(new TimeSpan(0, hours, minutes, seconds, milliseconds), Convert.ToInt64(parts[6].Trim(), 16)); + } + } + return null; + } + + } +} diff --git a/libse/VobSub/IdxParagraph.cs b/libse/VobSub/IdxParagraph.cs new file mode 100644 index 000000000..0fc9799f1 --- /dev/null +++ b/libse/VobSub/IdxParagraph.cs @@ -0,0 +1,17 @@ +using System; + +namespace Nikse.SubtitleEdit.Core.VobSub +{ + public class IdxParagraph + { + public TimeSpan StartTime { get; private set; } + + public long FilePosition { get; private set; } + + public IdxParagraph(TimeSpan startTime, long filePosition) + { + StartTime = startTime; + FilePosition = filePosition; + } + } +} diff --git a/libse/VobSub/Mpeg2Header.cs b/libse/VobSub/Mpeg2Header.cs new file mode 100644 index 000000000..0cd29f9e0 --- /dev/null +++ b/libse/VobSub/Mpeg2Header.cs @@ -0,0 +1,36 @@ +using System; + +namespace Nikse.SubtitleEdit.Core.VobSub +{ + /// + /// http://www.mpucoder.com/DVD/packhdr.html + /// + public class Mpeg2Header + { + public const int Length = 14; + + public readonly UInt32 StartCode; + public readonly byte PackIndentifier; + //public readonly UInt64 SystemClockReferenceQuotient; + //public readonly UInt64 SystemClockReferenceRemainder; + public readonly UInt64 ProgramMuxRate; + public readonly int PackStuffingLength; + + public Mpeg2Header(byte[] buffer) + { + StartCode = Helper.GetEndian(buffer, 0, 3); + PackIndentifier = buffer[3]; + + //string b4To9AsBinary = Helper.GetBinaryString(buffer, 4, 6); + //b4To9AsBinary = b4To9AsBinary.Substring(2,3) + b4To9AsBinary.Substring(6,15) + b4To9AsBinary.Substring(22,15); + //SystemClockReferenceQuotient = Helper.GetUInt32FromBinaryString(b4To9AsBinary); + + //SystemClockReferenceRemainder = (ulong)(((buffer[8] & Helper.B00000011) << 8) + buffer[9]) + + ProgramMuxRate = Helper.GetEndian(buffer, 10, 3) >> 2; + + PackStuffingLength = buffer[13] & Helper.B00000111; + } + + } +} diff --git a/libse/VobSub/PacketizedElementaryStream.cs b/libse/VobSub/PacketizedElementaryStream.cs new file mode 100644 index 000000000..069b8471e --- /dev/null +++ b/libse/VobSub/PacketizedElementaryStream.cs @@ -0,0 +1,113 @@ +using System; +using System.IO; + +namespace Nikse.SubtitleEdit.Core.VobSub +{ + /// + /// http://www.mpucoder.com/DVD/pes-hdr.html + /// + public class PacketizedElementaryStream + { + public const int HeaderLength = 6; + + public readonly UInt32 StartCode; + public readonly int StreamId; + public readonly int Length; + public readonly int ScramblingControl; + public readonly int Priority; + public readonly int DataAlignmentIndicator; + public readonly int Copyright; + public readonly int OriginalOrCopy; + public readonly int PresentationTimestampDecodeTimestampFlags; + public readonly int ElementaryStreamClockReferenceFlag; + public readonly int EsRateFlag; + public readonly int DsmTrickModeFlag; + public readonly int AdditionalCopyInfoFlag; + public readonly int CrcFlag; + public readonly int ExtensionFlag; + public readonly int HeaderDataLength; + + public readonly UInt64? PresentationTimestamp; + public readonly UInt64? DecodeTimestamp; + + public readonly int? SubPictureStreamId; + + private readonly byte[] _dataBuffer; + + public PacketizedElementaryStream(byte[] buffer, int index) + { + StartCode = Helper.GetEndian(buffer, index + 0, 3); + StreamId = buffer[index + 3]; + Length = Helper.GetEndianWord(buffer, index + 4); + + ScramblingControl = (buffer[index + 6] >> 4) & Helper.B00000011; + Priority = buffer[index + 6] & Helper.B00001000; + DataAlignmentIndicator = buffer[index + 6] & Helper.B00000100; + Copyright = buffer[index + 6] & Helper.B00000010; + OriginalOrCopy = buffer[index + 6] & Helper.B00000001; + PresentationTimestampDecodeTimestampFlags = buffer[index + 7] >> 6; + ElementaryStreamClockReferenceFlag = buffer[index + 7] & Helper.B00100000; + EsRateFlag = buffer[index + 7] & Helper.B00010000; + DsmTrickModeFlag = buffer[index + 7] & Helper.B00001000; + AdditionalCopyInfoFlag = buffer[index + 7] & Helper.B00000100; + CrcFlag = buffer[index + 7] & Helper.B00001000; + ExtensionFlag = buffer[index + 7] & Helper.B00000010; + + HeaderDataLength = buffer[index + 8]; + + if (StreamId == 0xBD) + { + int id = buffer[index + 9 + HeaderDataLength]; + if (id >= 0x20 && id <= 0x40) // x3f 0r x40 ? + SubPictureStreamId = id; + } + + int tempIndex = index + 9; + if (PresentationTimestampDecodeTimestampFlags == Helper.B00000010 || + PresentationTimestampDecodeTimestampFlags == Helper.B00000011) + { + PresentationTimestamp = (ulong)buffer[tempIndex + 4] >> 1; + PresentationTimestamp += (ulong)buffer[tempIndex + 3] << 7; + PresentationTimestamp += (ulong)(buffer[tempIndex + 2] & Helper.B11111110) << 14; + PresentationTimestamp += (ulong)buffer[tempIndex + 1] << 22; + PresentationTimestamp += (ulong)(buffer[tempIndex + 0] & Helper.B00001110) << 29; + + //string bString = Helper.GetBinaryString(buffer, tempIndex, 5); + //bString = bString.Substring(4, 3) + bString.Substring(8, 15) + bString.Substring(24, 15); + //PresentationTimestamp = Convert.ToUInt64(bString, 2); + tempIndex += 5; + } + if (PresentationTimestampDecodeTimestampFlags == Helper.B00000011) + { + //string bString = Helper.GetBinaryString(buffer, tempIndex, 5); + //bString = bString.Substring(4, 3) + bString.Substring(8, 15) + bString.Substring(24, 15); + //DecodeTimestamp = Convert.ToUInt64(bString, 2); + + DecodeTimestamp = (ulong)buffer[tempIndex + 4] >> 1; + DecodeTimestamp += (ulong)buffer[tempIndex + 3] << 7; + DecodeTimestamp += (ulong)(buffer[tempIndex + 2] & Helper.B11111110) << 14; + DecodeTimestamp += (ulong)buffer[tempIndex + 1] << 22; + DecodeTimestamp += (ulong)(buffer[tempIndex + 0] & Helper.B00001110) << 29; + } + + int dataIndex = index + HeaderDataLength + 24 - Mpeg2Header.Length; + int dataSize = Length - (4 + HeaderDataLength); + + if (dataSize < 0 || (dataSize + dataIndex > buffer.Length)) // to fix bad subs... + { + dataSize = buffer.Length - dataIndex; + if (dataSize < 0) + return; + } + + _dataBuffer = new byte[dataSize]; + Buffer.BlockCopy(buffer, dataIndex, _dataBuffer, 0, dataSize); + } + + public void WriteToStream(Stream stream) + { + stream.Write(_dataBuffer, 0, _dataBuffer.Length); + } + + } +} diff --git a/libse/VobSub/SpHeader.cs b/libse/VobSub/SpHeader.cs new file mode 100644 index 000000000..dfbf41290 --- /dev/null +++ b/libse/VobSub/SpHeader.cs @@ -0,0 +1,31 @@ +using System; + +namespace Nikse.SubtitleEdit.Core.VobSub +{ + public class SpHeader + { + public const int SpHeaderLength = 14; + + public string Identifier { get; private set; } + public TimeSpan StartTime { get; private set; } + public int NextBlockPosition { get; private set; } + public int ControlSequencePosition { get; private set; } + public SubPicture Picture { get; private set; } + + public SpHeader(byte[] buffer) + { + Identifier = System.Text.Encoding.ASCII.GetString(buffer, 0, 2); + int startMilliseconds = (int)Helper.GetLittleEndian32(buffer, 2) / 90; + StartTime = TimeSpan.FromMilliseconds(startMilliseconds); + NextBlockPosition = Helper.GetEndianWord(buffer, 10) - 4; + ControlSequencePosition = Helper.GetEndianWord(buffer, 12) - 4; + } + + public SubPicture AddPicture(byte[] buffer) + { + Picture = new SubPicture(buffer, ControlSequencePosition, -4); + return Picture; + } + + } +} diff --git a/libse/VobSub/SubPicture.cs b/libse/VobSub/SubPicture.cs new file mode 100644 index 000000000..1373d775d --- /dev/null +++ b/libse/VobSub/SubPicture.cs @@ -0,0 +1,511 @@ +using System; +using System.Collections.Generic; +using System.Drawing; + +namespace Nikse.SubtitleEdit.Core.VobSub +{ + /// + /// Subtitle Picture - see http://www.mpucoder.com/DVD/spu.html for more info + /// + public class SubPicture + { + private enum DisplayControlCommand + { + ForcedStartDisplay = 0, + StartDisplay = 1, + StopDisplay = 2, + SetColor = 3, + SetContrast = 4, + SetDisplayArea = 5, + SetPixelDataAddress = 6, + ChangeColorAndContrast = 7, + End = 0xFF, + } + + public readonly int SubPictureDateSize; + public TimeSpan Delay; + public int BufferSize { get { return _data.Length; } } + private readonly byte[] _data; + public Rectangle ImageDisplayArea; + public bool Forced { get; private set; } + private int _pixelDataAddressOffset; + private int _startDisplayControlSequenceTableAddress; + + public SubPicture(byte[] data) + { + _data = data; + SubPictureDateSize = Helper.GetEndianWord(_data, 0); + _startDisplayControlSequenceTableAddress = Helper.GetEndianWord(_data, 2); + ParseDisplayControlCommands(false, null, null, false); + } + + /// + /// For SP packet with DVD subpictures + /// + /// Byte data buffer + /// Address of first control sequence in data + /// Bitmap pixel data address offset + public SubPicture(byte[] data, int startDisplayControlSequenceTableAddress, int pixelDataAddressOffset) + { + _data = data; + SubPictureDateSize = _data.Length; + _startDisplayControlSequenceTableAddress = startDisplayControlSequenceTableAddress; + _pixelDataAddressOffset = pixelDataAddressOffset; + ParseDisplayControlCommands(false, null, null, false); + } + + /// + /// Generates the current subtitle image + /// + /// The Color LookUp Table (CLUT), if null then only the four colors are used (should contain 16 elements if not null) + /// Background color + /// Color + /// Color + /// Color + /// Use custom colors instead of lookup table + /// Subtitle image + public Bitmap GetBitmap(List colorLookupTable, Color background, Color pattern, Color emphasis1, Color emphasis2, bool useCustomColors) + { + var fourColors = new List { background, pattern, emphasis1, emphasis2 }; + return ParseDisplayControlCommands(true, colorLookupTable, fourColors, useCustomColors); + } + + private Bitmap ParseDisplayControlCommands(bool createBitmap, List colorLookUpTable, List fourColors, bool useCustomColors) + { + ImageDisplayArea = new Rectangle(); + Bitmap bmp = null; + var displayControlSequenceTableAddresses = new List(); + int imageTopFieldDataAddress = 0; + int imageBottomFieldDataAddress = 0; + bool bitmapGenerated = false; + double largestDelay = -999999; + int displayControlSequenceTableAddress = _startDisplayControlSequenceTableAddress - _pixelDataAddressOffset; + int lastDisplayControlSequenceTableAddress = 0; + displayControlSequenceTableAddresses.Add(displayControlSequenceTableAddress); + int commandIndex = 0; + while (displayControlSequenceTableAddress > lastDisplayControlSequenceTableAddress && displayControlSequenceTableAddress + 1 < _data.Length && commandIndex < _data.Length) + { + int delayBeforeExecute = Helper.GetEndianWord(_data, displayControlSequenceTableAddress + _pixelDataAddressOffset); + commandIndex = displayControlSequenceTableAddress + 4 + _pixelDataAddressOffset; + if (commandIndex >= _data.Length) + break; // invalid index + + int command = _data[commandIndex]; + int numberOfCommands = 0; + while (command != (int)DisplayControlCommand.End && numberOfCommands < 1000 && commandIndex < _data.Length) + { + numberOfCommands++; + switch (command) + { + case (int)DisplayControlCommand.ForcedStartDisplay: // 0 + Forced = true; + commandIndex++; + break; + case (int)DisplayControlCommand.StartDisplay: // 1 + commandIndex++; + break; + case (int)DisplayControlCommand.StopDisplay: // 2 + Delay = TimeSpan.FromMilliseconds(((delayBeforeExecute << 10) + 1023) / 90.0); + if (createBitmap && Delay.TotalMilliseconds > largestDelay) // in case of more than one images, just use the one with the largest display time + { + largestDelay = Delay.TotalMilliseconds; + if (bmp != null) + { + bmp.Dispose(); + } + bmp = GenerateBitmap(ImageDisplayArea, imageTopFieldDataAddress, imageBottomFieldDataAddress, fourColors); + bitmapGenerated = true; + } + commandIndex++; + break; + case (int)DisplayControlCommand.SetColor: // 3 + if (colorLookUpTable != null && fourColors.Count == 4) + { + byte[] imageColor = { _data[commandIndex + 1], _data[commandIndex + 2] }; + if (!useCustomColors) + { + SetColor(fourColors, 3, imageColor[0] >> 4, colorLookUpTable); + SetColor(fourColors, 2, imageColor[0] & Helper.B00001111, colorLookUpTable); + SetColor(fourColors, 1, imageColor[1] >> 4, colorLookUpTable); + SetColor(fourColors, 0, imageColor[1] & Helper.B00001111, colorLookUpTable); + } + } + commandIndex += 3; + break; + case (int)DisplayControlCommand.SetContrast: // 4 + if (colorLookUpTable != null && fourColors.Count == 4) + { + var imageContrast = new[] { _data[commandIndex + 1], _data[commandIndex + 2] }; + if (imageContrast[0] + imageContrast[1] > 0) + { + SetTransparency(fourColors, 3, (imageContrast[0] & 0xF0) >> 4); + SetTransparency(fourColors, 2, imageContrast[0] & Helper.B00001111); + SetTransparency(fourColors, 1, (imageContrast[1] & 0xF0) >> 4); + SetTransparency(fourColors, 0, imageContrast[1] & Helper.B00001111); + } + } + commandIndex += 3; + break; + case (int)DisplayControlCommand.SetDisplayArea: // 5 + if (_data.Length > commandIndex + 6) + { + int startingX = (_data[commandIndex + 1] << 8 | _data[commandIndex + 2]) >> 4; + int endingX = (_data[commandIndex + 2] & Helper.B00001111) << 8 | _data[commandIndex + 3]; + int startingY = (_data[commandIndex + 4] << 8 | _data[commandIndex + 5]) >> 4; + int endingY = (_data[commandIndex + 5] & Helper.B00001111) << 8 | _data[commandIndex + 6]; + ImageDisplayArea = new Rectangle(startingX, startingY, endingX - startingX, endingY - startingY); + } + commandIndex += 7; + break; + case (int)DisplayControlCommand.SetPixelDataAddress: // 6 + imageTopFieldDataAddress = Helper.GetEndianWord(_data, commandIndex + 1) + _pixelDataAddressOffset; + imageBottomFieldDataAddress = Helper.GetEndianWord(_data, commandIndex + 3) + _pixelDataAddressOffset; + commandIndex += 5; + break; + case (int)DisplayControlCommand.ChangeColorAndContrast: // 7 + commandIndex++; + //int parameterAreaSize = (int)Helper.GetEndian(_data, commandIndex, 2); + if (commandIndex + 1 < _data.Length) + { + int parameterAreaSize = _data[commandIndex + 1]; // this should be enough??? (no larger than 255 bytes) + if (colorLookUpTable != null) + { + // TODO: Set fourColors + } + commandIndex += parameterAreaSize; + } + else + { + commandIndex++; + } + break; + default: + commandIndex++; + break; + } + if (commandIndex >= _data.Length) // in case of bad files... + break; + command = _data[commandIndex]; + } + + lastDisplayControlSequenceTableAddress = displayControlSequenceTableAddress; + if (_pixelDataAddressOffset == -4) + displayControlSequenceTableAddress = Helper.GetEndianWord(_data, commandIndex + 3); + else + displayControlSequenceTableAddress = Helper.GetEndianWord(_data, displayControlSequenceTableAddress + 2); + } + if (createBitmap && !bitmapGenerated) // StopDisplay not needed (delay will be zero - should be just before start of next subtitle) + bmp = GenerateBitmap(ImageDisplayArea, imageTopFieldDataAddress, imageBottomFieldDataAddress, fourColors); + + return bmp; + } + + private static void SetColor(List fourColors, int fourColorIndex, int clutIndex, List colorLookUpTable) + { + if (clutIndex >= 0 && clutIndex < colorLookUpTable.Count && fourColorIndex >= 0) + fourColors[fourColorIndex] = colorLookUpTable[clutIndex]; + } + + private static void SetTransparency(List fourColors, int fourColorIndex, int alpha) + { + // alpha: 0x0 = transparent, 0xF = opaque (in C# 0 is fully transparent, and 255 is fully opaque so we have to multiply by 17) + + if (fourColorIndex >= 0) + fourColors[fourColorIndex] = Color.FromArgb(alpha * 17, fourColors[fourColorIndex].R, fourColors[fourColorIndex].G, fourColors[fourColorIndex].B); + } + + private Bitmap GenerateBitmap(Rectangle imageDisplayArea, int imageTopFieldDataAddress, int imageBottomFieldDataAddress, List fourColors) + { + if (imageDisplayArea.Width <= 0 || imageDisplayArea.Height <= 0) + return new Bitmap(1, 1); + + var bmp = new Bitmap(imageDisplayArea.Width + 1, imageDisplayArea.Height + 1); + if (fourColors[0] != Color.Transparent) + { + var gr = Graphics.FromImage(bmp); + gr.FillRectangle(new SolidBrush(fourColors[0]), new Rectangle(0, 0, bmp.Width, bmp.Height)); + gr.Dispose(); + } + var fastBmp = new FastBitmap(bmp); + fastBmp.LockImage(); + GenerateBitmap(_data, fastBmp, 0, imageTopFieldDataAddress, fourColors, 2); + GenerateBitmap(_data, fastBmp, 1, imageBottomFieldDataAddress, fourColors, 2); + Bitmap cropped = CropBitmapAndUnlok(fastBmp, fourColors[0]); + bmp.Dispose(); + return cropped; + } + + private static Bitmap CropBitmapAndUnlok(FastBitmap bmp, Color backgroundColor) + { + int y = 0; + int x; + Color c = backgroundColor; + int backgroundArgb = backgroundColor.ToArgb(); + + // Crop top + while (y < bmp.Height && IsBackgroundColor(c, backgroundArgb)) + { + c = bmp.GetPixel(0, y); + if (IsBackgroundColor(c, backgroundArgb)) + { + for (x = 1; x < bmp.Width; x++) + { + c = bmp.GetPixelNext(); + if (c.A != 0 && c.ToArgb() != backgroundArgb) + break; + } + } + if (IsBackgroundColor(c, backgroundArgb)) + y++; + } + int minY = y; + if (minY > 3) + minY -= 3; + else + minY = 0; + + // Crop left + x = 0; + c = backgroundColor; + while (x < bmp.Width && IsBackgroundColor(c, backgroundArgb)) + { + for (y = minY; y < bmp.Height; y++) + { + c = bmp.GetPixel(x, y); + if (!IsBackgroundColor(c, backgroundArgb)) + break; + } + if (IsBackgroundColor(c, backgroundArgb)) + x++; + } + int minX = x; + if (minX > 3) + minX -= 3; + else + minX -= 0; + + // Crop bottom + y = bmp.Height - 1; + c = backgroundColor; + while (y > minY && IsBackgroundColor(c, backgroundArgb)) + { + c = bmp.GetPixel(0, y); + if (IsBackgroundColor(c, backgroundArgb)) + { + for (x = 1; x < bmp.Width; x++) + { + c = bmp.GetPixelNext(); + if (!IsBackgroundColor(c, backgroundArgb)) + break; + } + } + if (IsBackgroundColor(c, backgroundArgb)) + y--; + } + int maxY = y + 7; + if (maxY >= bmp.Height) + maxY = bmp.Height - 1; + + // Crop right + x = bmp.Width - 1; + c = backgroundColor; + while (x > minX && IsBackgroundColor(c, backgroundArgb)) + { + for (y = minY; y < bmp.Height; y++) + { + c = bmp.GetPixel(x, y); + if (!IsBackgroundColor(c, backgroundArgb)) + break; + } + if (IsBackgroundColor(c, backgroundArgb)) + x--; + } + int maxX = x + 7; + if (maxX >= bmp.Width) + maxX = bmp.Width - 1; + + bmp.UnlockImage(); + Bitmap bmpImage = bmp.GetBitmap(); + if (bmpImage.Width > 1 && bmpImage.Height > 1 && maxX - minX > 0 && maxY - minY > 0) + { + Bitmap bmpCrop = bmpImage.Clone(new Rectangle(minX, minY, maxX - minX, maxY - minY), bmpImage.PixelFormat); + return bmpCrop; + } + return (Bitmap)bmpImage.Clone(); + } + + private static bool IsBackgroundColor(Color c, int backgroundArgb) + { + return c.A == 0 || c.ToArgb() == backgroundArgb; + } + + public static void GenerateBitmap(byte[] data, FastBitmap bmp, int startY, int dataAddress, List fourColors, int addY) + { + int index = 0; + bool onlyHalf = false; + int y = startY; + int x = 0; + int colorZeroValue = fourColors[0].ToArgb(); + while (y < bmp.Height && dataAddress + index + 2 < data.Length) + { + int runLength; + int color; + bool restOfLine; + index += DecodeRle(dataAddress + index, data, out color, out runLength, ref onlyHalf, out restOfLine); + if (restOfLine) + runLength = bmp.Width - x; + + Color c = fourColors[color]; // set color via the four colors + for (int i = 0; i < runLength; i++, x++) + { + if (x >= bmp.Width - 1) + { + if (y < bmp.Height && x < bmp.Width && c != fourColors[0]) + bmp.SetPixel(x, y, c); + + if (onlyHalf) + { + onlyHalf = false; + index++; + } + x = 0; + y += addY; + break; + } + if (y < bmp.Height && c.ToArgb() != colorZeroValue) + bmp.SetPixel(x, y, c); + } + } + } + + private static int DecodeRle(int index, byte[] data, out int color, out int runLength, ref bool onlyHalf, out bool restOfLine) + { + //Value Bits n=length, c=color + //1-3 4 nncc (half a byte) + //4-15 8 00nnnncc (one byte) + //16-63 12 0000nnnnnncc (one and a half byte) + //64-255 16 000000nnnnnnnncc (two bytes) + // When reaching EndOfLine, index is byte aligned (skip 4 bits if necessary) + restOfLine = false; + byte b1 = data[index]; + byte b2 = data[index + 1]; + + if (onlyHalf) + { + byte b3 = data[index + 2]; + b1 = (byte)(((b1 & Helper.B00001111) << 4) | ((b2 & Helper.B11110000) >> 4)); + b2 = (byte)(((b2 & Helper.B00001111) << 4) | ((b3 & Helper.B11110000) >> 4)); + } + + if (b1 >> 2 == 0) + { + runLength = (b1 << 6) | (b2 >> 2); + color = b2 & Helper.B00000011; + if (runLength == 0) + { + // rest of line + skip 4 bits if Only half + restOfLine = true; + if (onlyHalf) + { + onlyHalf = false; + return 3; + } + } + return 2; + } + + if (b1 >> 4 == 0) + { + runLength = (b1 << 2) | (b2 >> 6); + color = (b2 & Helper.B00110000) >> 4; + if (onlyHalf) + { + onlyHalf = false; + return 2; + } + onlyHalf = true; + return 1; + } + + if (b1 >> 6 == 0) + { + runLength = b1 >> 2; + color = b1 & Helper.B00000011; + return 1; + } + + runLength = b1 >> 6; + color = (b1 & Helper.B00110000) >> 4; + + if (onlyHalf) + { + onlyHalf = false; + return 1; + } + onlyHalf = true; + return 0; + } + + //private static int DecodeRleNonOptimized(int index, byte[] data, out int color, out int runLength, ref bool onlyHalf, out bool restOfLine) + //{ + // //Value Bits n=length, c=color + // //1-3 4 n n c c (half a byte) + // //4-15 8 0 0 n n n n c c (one byte) + // //16-63 12 0 0 0 0 n n n n n n c c (one and a half byte) + // //64-255 16 0 0 0 0 0 0 n n n n n n n n c c (two bytes) + // // When reaching EndOfLine, index is byte aligned (skip 4 bits if necessary) + // restOfLine = false; + // string binary2 = Helper.GetBinaryString(data, index, 3); + // if (onlyHalf) + // binary2 = binary2.Substring(4); + + // if (binary2.StartsWith("000000")) + // { + // runLength = (int)Helper.GetUInt32FromBinaryString(binary2.Substring(6, 8)); + // color = (int)Helper.GetUInt32FromBinaryString(binary2.Substring(14, 2)); + // if (runLength == 0) + // { + // // rest of line + skip 4 bits if Only half + // restOfLine = true; + // if (onlyHalf) + // { + // onlyHalf = false; + // return 3; + // } + // } + // return 2; + // } + + // if (binary2.StartsWith("0000")) + // { + // runLength = (int)Helper.GetUInt32FromBinaryString(binary2.Substring(4, 6)); + // color = (int)Helper.GetUInt32FromBinaryString(binary2.Substring(10, 2)); + // if (onlyHalf) + // { + // onlyHalf = false; + // return 2; + // } + // onlyHalf = true; + // return 1; + // } + + // if (binary2.StartsWith("00")) + // { + // runLength = (int)Helper.GetUInt32FromBinaryString(binary2.Substring(2, 4)); + // color = (int)Helper.GetUInt32FromBinaryString(binary2.Substring(6, 2)); + // return 1; + // } + + // runLength = (int)Helper.GetUInt32FromBinaryString(binary2.Substring(0, 2)); + // color = (int)Helper.GetUInt32FromBinaryString(binary2.Substring(2, 2)); + // if (onlyHalf) + // { + // onlyHalf = false; + // return 1; + // } + // onlyHalf = true; + // return 0; + //} + + } +} diff --git a/libse/VobSub/VobSubMergedPack.cs b/libse/VobSub/VobSubMergedPack.cs new file mode 100644 index 000000000..e638ebbe9 --- /dev/null +++ b/libse/VobSub/VobSubMergedPack.cs @@ -0,0 +1,21 @@ +using System; + +namespace Nikse.SubtitleEdit.Core.VobSub +{ + public class VobSubMergedPack + { + public SubPicture SubPicture { get; private set; } + public TimeSpan StartTime { get; private set; } + public TimeSpan EndTime { get; set; } + public int StreamId { get; private set; } + public IdxParagraph IdxLine { get; private set; } + + public VobSubMergedPack(byte[] subPictureData, TimeSpan presentationTimestamp, int streamId, IdxParagraph idxLine) + { + SubPicture = new SubPicture(subPictureData); + StartTime = presentationTimestamp; + StreamId = streamId; + IdxLine = idxLine; + } + } +} diff --git a/libse/VobSub/VobSubPack.cs b/libse/VobSub/VobSubPack.cs new file mode 100644 index 000000000..2988a4e89 --- /dev/null +++ b/libse/VobSub/VobSubPack.cs @@ -0,0 +1,35 @@ +namespace Nikse.SubtitleEdit.Core.VobSub +{ + public class VobSubPack + { + public PacketizedElementaryStream PacketizedElementaryStream; + public Mpeg2Header Mpeg2Header; + public IdxParagraph IdxLine { get; private set; } + + private readonly byte[] _buffer; + + public byte[] Buffer + { + get + { + return _buffer; + } + } + + public VobSubPack(byte[] buffer, IdxParagraph idxLine) + { + _buffer = buffer; + IdxLine = idxLine; + + if (VobSubParser.IsMpeg2PackHeader(buffer)) + { + Mpeg2Header = new Mpeg2Header(buffer); + PacketizedElementaryStream = new PacketizedElementaryStream(buffer, Mpeg2Header.Length); + } + else if (VobSubParser.IsPrivateStream1(buffer, 0)) + { + PacketizedElementaryStream = new PacketizedElementaryStream(buffer, 0); + } + } + } +} diff --git a/libse/VobSub/VobSubParser.cs b/libse/VobSub/VobSubParser.cs new file mode 100644 index 000000000..04a6bbe8c --- /dev/null +++ b/libse/VobSub/VobSubParser.cs @@ -0,0 +1,247 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; + +namespace Nikse.SubtitleEdit.Core.VobSub +{ + public class VobSubParser + { + public bool IsPal { get; private set; } + public List VobSubPacks { get; private set; } + public List IdxPalette = new List(); + public List IdxLanguages = new List(); + + private const int PacketizedElementaryStreamMaximumLength = 2028; + + public VobSubParser(bool isPal) + { + IsPal = isPal; + VobSubPacks = new List(); + } + + public void Open(string fileName) + { + using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + Open(fs); + } + } + + /// + /// Can be used with e.g. MemoryStream or FileStream + /// + /// + public void Open(Stream ms) + { + VobSubPacks = new List(); + ms.Position = 0; + var buffer = new byte[0x800]; // 2048 + long position = 0; + while (position < ms.Length) + { + ms.Seek(position, SeekOrigin.Begin); + ms.Read(buffer, 0, 0x0800); + if (IsSubtitlePack(buffer)) + VobSubPacks.Add(new VobSubPack(buffer, null)); + + position += 0x800; + } + } + + public void OpenSubIdx(string vobSubFileName, string idxFileName) + { + VobSubPacks = new List(); + + if (!string.IsNullOrEmpty(idxFileName) && File.Exists(idxFileName)) + { + var idx = new Idx(idxFileName); + IdxPalette = idx.Palette; + IdxLanguages = idx.Languages; + if (idx.IdxParagraphs.Count > 0) + { + var buffer = new byte[0x800]; // 2048 + using (var fs = new FileStream(vobSubFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + foreach (var p in idx.IdxParagraphs) + { + if (p.FilePosition + 100 < fs.Length) + { + long position = p.FilePosition; + fs.Seek(position, SeekOrigin.Begin); + fs.Read(buffer, 0, 0x0800); + if (IsSubtitlePack(buffer) || IsPrivateStream1(buffer, 0)) + { + var vsp = new VobSubPack(buffer, p); + VobSubPacks.Add(vsp); + + if (IsPrivateStream1(buffer, 0)) + position += vsp.PacketizedElementaryStream.Length + 6; + else + position += 0x800; + + int currentSubPictureStreamId = vsp.PacketizedElementaryStream.SubPictureStreamId.Value; + while (vsp.PacketizedElementaryStream != null && + vsp.PacketizedElementaryStream.SubPictureStreamId.HasValue && + (vsp.PacketizedElementaryStream.Length == PacketizedElementaryStreamMaximumLength || + currentSubPictureStreamId != vsp.PacketizedElementaryStream.SubPictureStreamId.Value) && position < fs.Length) + { + fs.Seek(position, SeekOrigin.Begin); + fs.Read(buffer, 0, 0x800); + vsp = new VobSubPack(buffer, p); // idx position? + + if (vsp.PacketizedElementaryStream != null && vsp.PacketizedElementaryStream.SubPictureStreamId.HasValue && currentSubPictureStreamId == vsp.PacketizedElementaryStream.SubPictureStreamId.Value) + { + VobSubPacks.Add(vsp); + + if (IsPrivateStream1(buffer, 0)) + position += vsp.PacketizedElementaryStream.Length + 6; + else + position += 0x800; + } + else + { + position += 0x800; + fs.Seek(position, SeekOrigin.Begin); + } + } + } + } + } + } + return; + } + } + + // No valid idx file found - just open like vob file + Open(vobSubFileName); + } + + /// + /// Demultiplex multiplexed packs together each streamId at a time + removing bad packs + fixing displaytimes + /// + /// List of complete packs each with a complete sub image + public List MergeVobSubPacks() + { + var list = new List(); + var pts = new TimeSpan(); + var ms = new MemoryStream(); + int streamId = 0; + + float ticksPerMillisecond = 90.000F; + if (!IsPal) + ticksPerMillisecond = 90.090F; // TODO: What should this be for NTSC? + + // get unique streamIds + var uniqueStreamIds = new List(); + foreach (var p in VobSubPacks) + { + if (p.PacketizedElementaryStream != null && + p.PacketizedElementaryStream.SubPictureStreamId.HasValue && + !uniqueStreamIds.Contains(p.PacketizedElementaryStream.SubPictureStreamId.Value)) + uniqueStreamIds.Add(p.PacketizedElementaryStream.SubPictureStreamId.Value); + } + + IdxParagraph lastIdxParagraph = null; + foreach (int uniqueStreamId in uniqueStreamIds) // packets must be merged in streamId order (so they don't get mixed) + { + foreach (var p in VobSubPacks) + { + if (p.PacketizedElementaryStream != null && p.PacketizedElementaryStream.SubPictureStreamId.HasValue && + p.PacketizedElementaryStream.SubPictureStreamId.Value == uniqueStreamId) + { + if (p.PacketizedElementaryStream.PresentationTimestampDecodeTimestampFlags > 0) + { + if (lastIdxParagraph == null || p.IdxLine.FilePosition != lastIdxParagraph.FilePosition) + { + if (ms.Length > 0) + list.Add(new VobSubMergedPack(ms.ToArray(), pts, streamId, lastIdxParagraph)); + ms.Close(); + ms = new MemoryStream(); + pts = TimeSpan.FromMilliseconds(Convert.ToDouble(p.PacketizedElementaryStream.PresentationTimestamp / ticksPerMillisecond)); //90000F * 1000)); (PAL) + streamId = p.PacketizedElementaryStream.SubPictureStreamId.Value; + } + } + lastIdxParagraph = p.IdxLine; + p.PacketizedElementaryStream.WriteToStream(ms); + } + } + if (ms.Length > 0) + { + list.Add(new VobSubMergedPack(ms.ToArray(), pts, streamId, lastIdxParagraph)); + ms.Close(); + ms = new MemoryStream(); + } + } + ms.Close(); + + // Remove any bad packs + for (int i = list.Count - 1; i >= 0; i--) + { + VobSubMergedPack pack = list[i]; + if (pack.SubPicture == null || pack.SubPicture.ImageDisplayArea.Width <= 3 || pack.SubPicture.ImageDisplayArea.Height <= 2) + list.RemoveAt(i); + else if (pack.EndTime.TotalSeconds - pack.StartTime.TotalSeconds < 0.1 && pack.SubPicture.ImageDisplayArea.Width <= 10 && pack.SubPicture.ImageDisplayArea.Height <= 10) + list.RemoveAt(i); + } + + // Fix subs with no duration (completely normal) or negative duration or duration > 10 seconds + for (int i = 0; i < list.Count; i++) + { + VobSubMergedPack pack = list[i]; + if (pack.SubPicture.Delay.TotalMilliseconds > 0) + pack.EndTime = pack.StartTime.Add(pack.SubPicture.Delay); + + if (pack.EndTime < pack.StartTime || pack.EndTime.TotalSeconds - pack.StartTime.TotalSeconds > 10.0) + { + if (i + 1 < list.Count) + pack.EndTime = TimeSpan.FromMilliseconds(list[i].StartTime.TotalMilliseconds - 100); + else + pack.EndTime = TimeSpan.FromMilliseconds(pack.StartTime.TotalMilliseconds + 3000); + } + } + + return list; + } + + public static bool IsMpeg2PackHeader(byte[] buffer) + { + return buffer.Length >= 3 && + buffer[0] == 0 && + buffer[1] == 0 && + buffer[2] == 1 && + buffer[3] == 0xba; // 0xba == 186 - MPEG-2 Pack Header + } + + public static bool IsPrivateStream1(byte[] buffer, int index) + { + return buffer.Length >= index + 3 && + buffer[index + 0] == 0 && + buffer[index + 1] == 0 && + buffer[index + 2] == 1 && + buffer[index + 3] == 0xbd; // 0xbd == 189 - MPEG-2 Private stream 1 (non MPEG audio, subpictures) + } + + public static bool IsPrivateStream2(byte[] buffer, int index) + { + return buffer.Length >= index + 3 && + buffer[index + 0] == 0 && + buffer[index + 1] == 0 && + buffer[index + 2] == 1 && + buffer[index + 3] == 0xbf; // 0xbf == 191 - MPEG-2 Private stream 2 + } + + public static bool IsSubtitlePack(byte[] buffer) + { + if (IsMpeg2PackHeader(buffer) && IsPrivateStream1(buffer, Mpeg2Header.Length)) + { + int pesHeaderDataLength = buffer[Mpeg2Header.Length + 8]; + int streamId = buffer[Mpeg2Header.Length + 8 + 1 + pesHeaderDataLength]; + if (streamId >= 0x20 && streamId <= 0x3f) // Subtitle IDs allowed (or x3f to x40?) + return true; + } + return false; + } + + } +} \ No newline at end of file diff --git a/libse/VobSub/VobSubWriter.cs b/libse/VobSub/VobSubWriter.cs new file mode 100644 index 000000000..5ba0a7b77 --- /dev/null +++ b/libse/VobSub/VobSubWriter.cs @@ -0,0 +1,444 @@ +using System; +using System.Drawing; +using System.IO; +using System.Text; + +namespace Nikse.SubtitleEdit.Core.VobSub +{ + public class VobSubWriter : IDisposable + { + + private class MemWriter + { + private readonly byte[] _buf; + private long _pos; + + public MemWriter(long size) + { + _buf = new byte[size]; + _pos = 0; + } + + public byte[] GetBuf() + { + return _buf; + } + + public long GetPosition() + { + return _pos; + } + + public void GotoBegin() + { + _pos = 0; + } + + public void WriteByte(byte val) + { + _buf[_pos++] = val; + } + } + + private readonly string _subFileName; + private FileStream _subFile; + private readonly StringBuilder _idx = new StringBuilder(); + private readonly int _screenWidth = 720; + private readonly int _screenHeight = 480; + private readonly int _bottomMargin = 15; + private readonly int _leftRightMargin = 15; + private readonly int _languageStreamId; + private Color _background = Color.Transparent; + private Color _pattern = Color.White; + private Color _emphasis1 = Color.Black; + private readonly bool _useInnerAntialiasing = true; + private Color _emphasis2 = Color.FromArgb(240, Color.Black); + private readonly string _languageName = "English"; + private readonly string _languageNameShort = "en"; + + public VobSubWriter(string subFileName, int screenWidth, int screenHeight, int bottomMargin, int leftRightMargin, int languageStreamId, Color pattern, Color emphasis1, bool useInnerAntialiasing, string languageName, string languageNameShort) + { + _subFileName = subFileName; + _screenWidth = screenWidth; + _screenHeight = screenHeight; + _bottomMargin = bottomMargin; + _leftRightMargin = leftRightMargin; + _languageStreamId = languageStreamId; + _pattern = pattern; + _emphasis1 = emphasis1; + _useInnerAntialiasing = useInnerAntialiasing; + _languageName = languageName; + _languageNameShort = languageNameShort; + _idx = CreateIdxHeader(); + _subFile = new FileStream(subFileName, FileMode.Create); + } + + public static void WriteEndianWord(int i, Stream stream) + { + stream.WriteByte((byte)(i / 256)); + stream.WriteByte((byte)(i % 256)); + } + + private byte[] GetSubImageBuffer(RunLengthTwoParts twoPartBuffer, NikseBitmap nbmp, Paragraph p, ContentAlignment alignment) + { + var ms = new MemoryStream(); + + // sup picture datasize + WriteEndianWord(twoPartBuffer.Length + 34, ms); + + // first display control sequence table address + int startDisplayControlSequenceTableAddress = twoPartBuffer.Length + 4; + WriteEndianWord(startDisplayControlSequenceTableAddress, ms); + + // Write image + const int imageTopFieldDataAddress = 4; + ms.Write(twoPartBuffer.Buffer1, 0, twoPartBuffer.Buffer1.Length); + int imageBottomFieldDataAddress = 4 + twoPartBuffer.Buffer1.Length; + ms.Write(twoPartBuffer.Buffer2, 0, twoPartBuffer.Buffer2.Length); + + // Write zero delay + ms.WriteByte(0); + ms.WriteByte(0); + + // next display control sequence table address (use current is last) + WriteEndianWord(startDisplayControlSequenceTableAddress + 24, ms); // start of display control sequence table address + + // Control command start + if (p.Forced) + ms.WriteByte(0); // ForcedStartDisplay==0 + else + ms.WriteByte(1); // StartDisplay==1 + + // Control command 3 = SetColor + WriteColors(ms); // 3 bytes + + // Control command 4 = SetContrast + WriteContrast(ms); // 3 bytes + + // Control command 5 = SetDisplayArea + WriteDisplayArea(ms, nbmp, alignment); // 7 bytes + + // Control command 6 = SetPixelDataAddress + WritePixelDataAddress(ms, imageTopFieldDataAddress, imageBottomFieldDataAddress); // 5 bytes + + // Control command exit + ms.WriteByte(255); // 1 byte + + // Control Sequence Table + // Write delay - subtitle duration + WriteEndianWord(Convert.ToInt32(p.Duration.TotalMilliseconds * 90.0 - 1023) >> 10, ms); + + // next display control sequence table address (use current is last) + WriteEndianWord(startDisplayControlSequenceTableAddress + 24, ms); // start of display control sequence table address + + // Control command 2 = StopDisplay + ms.WriteByte(2); + + // extra byte - for compatability with gpac/MP4BOX + ms.WriteByte(255); // 1 byte + + return ms.ToArray(); + } + + public void WriteParagraph(Paragraph p, Bitmap bmp, ContentAlignment alignment) // inspired by code from SubtitleCreator + { + // timestamp: 00:00:33:900, filepos: 000000000 + _idx.AppendLine(string.Format("timestamp: {0:00}:{1:00}:{2:00}:{3:000}, filepos: {4}", p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, p.StartTime.Milliseconds, _subFile.Position.ToString("X").PadLeft(9, '0').ToLower())); + + var nbmp = new NikseBitmap(bmp); + _emphasis2 = nbmp.ConverToFourColors(_background, _pattern, _emphasis1, _useInnerAntialiasing); + var twoPartBuffer = nbmp.RunLengthEncodeForDvd(_background, _pattern, _emphasis1, _emphasis2); + var imageBuffer = GetSubImageBuffer(twoPartBuffer, nbmp, p, alignment); + + int bufferIndex = 0; + byte vobSubId = (byte)_languageStreamId; + var mwsub = new MemWriter(200000); + byte[] subHeader = new byte[30]; + byte[] ts = new byte[4]; + + // Lended from "Son2VobSub" by Alain Vielle and Petr Vyskocil + // And also from Sup2VobSub by Emmel + subHeader[0] = 0x00; // MPEG 2 PACK HEADER + subHeader[1] = 0x00; + subHeader[2] = 0x01; + subHeader[3] = 0xba; + subHeader[4] = 0x44; + subHeader[5] = 0x02; + subHeader[6] = 0xc4; + subHeader[7] = 0x82; + subHeader[8] = 0x04; + subHeader[9] = 0xa9; + subHeader[10] = 0x01; + subHeader[11] = 0x89; + subHeader[12] = 0xc3; + subHeader[13] = 0xf8; + + subHeader[14] = 0x00; // PES + subHeader[15] = 0x00; + subHeader[16] = 0x01; + subHeader[17] = 0xbd; + + int packetSize = imageBuffer.Length; + long toWrite = packetSize; // Image buffer + control sequence length + bool header0 = true; + + while (toWrite > 0) + { + long headerSize; + if (header0) + { + header0 = false; + + // This is only for first packet + subHeader[20] = 0x81; // mark as original + subHeader[21] = 0x80; // first packet: PTS + subHeader[22] = 0x05; // PES header data length + + // PTS (90kHz): + //-------------- + subHeader[23] = (byte)((ts[3] & 0xc0) >> 5 | 0x21); + subHeader[24] = (byte)((ts[3] & 0x3f) << 2 | (ts[2] & 0xc0) >> 6); + subHeader[25] = (byte)((ts[2] & 0x3f) << 2 | (ts[1] & 0x80) >> 6 | 0x01); + subHeader[26] = (byte)((ts[1] & 0x7f) << 1 | (ts[0] & 0x80) >> 7); + subHeader[27] = (byte)((ts[0] & 0x7f) << 1 | 0x01); + + const string pre = "0010"; // 0011 or 0010 ? (KMPlayer will not understand 0011!!!) + long newPts = (long)(p.StartTime.TotalSeconds * 90000.0 + 0.5); + string bString = Convert.ToString(newPts, 2).PadLeft(33, '0'); + string fiveBytesString = pre + bString.Substring(0, 3) + "1" + bString.Substring(3, 15) + "1" + bString.Substring(18, 15) + "1"; + for (int i = 0; i < 5; i++) + { + subHeader[23 + i] = Convert.ToByte(fiveBytesString.Substring((i * 8), 8), 2); + } + subHeader[28] = vobSubId; + headerSize = 29; + } + else + { + subHeader[20] = 0x81; // mark as original + subHeader[21] = 0x00; // no PTS + subHeader[22] = 0x00; // header data length + subHeader[23] = vobSubId; + headerSize = 24; + } + + if ((toWrite + headerSize) <= 0x800) + { + // write whole image in one 0x800 part + + long j = (headerSize - 20) + toWrite; + subHeader[18] = (byte)(j / 0x100); + subHeader[19] = (byte)(j % 0x100); + + // First Write header + for (int x = 0; x < headerSize; x++) + mwsub.WriteByte(subHeader[x]); + + // Write Image Data + for (int x = 0; x < toWrite; x++) + mwsub.WriteByte(imageBuffer[bufferIndex++]); + + // Pad remaining space + long paddingSize = 0x800 - headerSize - toWrite; + for (int x = 0; x < paddingSize; x++) + mwsub.WriteByte(0xff); + + toWrite = 0; + } + else + { + // write multiple parts + + long blockSize = 0x800 - headerSize; + long j = (headerSize - 20) + blockSize; + subHeader[18] = (byte)(j / 0x100); + subHeader[19] = (byte)(j % 0x100); + + // First Write header + for (int x = 0; x < headerSize; x++) + mwsub.WriteByte(subHeader[x]); + + // Write Image Data + for (int x = 0; x < blockSize; x++) + mwsub.WriteByte(imageBuffer[bufferIndex++]); + + toWrite -= blockSize; + } + } + + // Write whole memory stream to file + long endPosition = mwsub.GetPosition(); + mwsub.GotoBegin(); + _subFile.Write(mwsub.GetBuf(), 0, (int)endPosition); + } + + private static void WritePixelDataAddress(Stream stream, int imageTopFieldDataAddress, int imageBottomFieldDataAddress) + { + stream.WriteByte(6); + WriteEndianWord(imageTopFieldDataAddress, stream); + WriteEndianWord(imageBottomFieldDataAddress, stream); + } + + private void WriteDisplayArea(Stream stream, NikseBitmap nbmp, ContentAlignment alignment) + { + stream.WriteByte(5); + + // Write 6 bytes of area - starting X, ending X, starting Y, ending Y, each 12 bits + ushort startX = (ushort)((_screenWidth - nbmp.Width) / 2); + ushort startY = (ushort)(_screenHeight - nbmp.Height - _bottomMargin); + + if (alignment == ContentAlignment.TopLeft || alignment == ContentAlignment.TopCenter || alignment == ContentAlignment.TopRight) + { + startY = (ushort)_bottomMargin; + } + if (alignment == ContentAlignment.MiddleLeft || alignment == ContentAlignment.MiddleCenter || alignment == ContentAlignment.MiddleRight) + { + startY = (ushort)((_screenHeight / 2) - (nbmp.Height / 2)); + } + if (alignment == ContentAlignment.TopLeft || alignment == ContentAlignment.MiddleLeft || alignment == ContentAlignment.BottomLeft) + { + startX = (ushort)_leftRightMargin; + } + if (alignment == ContentAlignment.TopRight || alignment == ContentAlignment.MiddleRight || alignment == ContentAlignment.BottomRight) + { + startX = (ushort)(_screenWidth - nbmp.Width - _leftRightMargin); + } + + ushort endX = (ushort)(startX + nbmp.Width - 1); + ushort endY = (ushort)(startY + nbmp.Height - 1); + + WriteEndianWord((ushort)(startX << 4 | endX >> 8), stream); // 16 - 12 start x + 4 end x + WriteEndianWord((ushort)(endX << 8 | startY >> 4), stream); // 16 - 8 endx + 8 starty + WriteEndianWord((ushort)(startY << 12 | endY), stream); // 16 - 4 start y + 12 end y + } + + /// + /// Directly provides the four contrast (alpha blend) values to associate with the four pixel values. One nibble per pixel value for a total of 2 bytes. 0x0 = transparent, 0xF = opaque + /// + private void WriteContrast(Stream stream) + { + stream.WriteByte(4); + stream.WriteByte((byte)((_emphasis2.A << 4) | _emphasis1.A)); // emphasis2 + emphasis1 + stream.WriteByte((byte)((_pattern.A << 4) | _background.A)); // pattern + background + } + + /// + /// provides four indices into the CLUT for the current PGC to associate with the four pixel values. One nibble per pixel value for a total of 2 bytes. + /// + private static void WriteColors(Stream stream) + { + // Index to palette + const byte emphasis2 = 3; + const byte emphasis1 = 2; + const byte pattern = 1; + const byte background = 0; + + stream.WriteByte(3); + stream.WriteByte((emphasis2 << 4) | emphasis1); // emphasis2 + emphasis1 + stream.WriteByte((pattern << 4) | background); // pattern + background + } + + public void WriteIdxFile() + { + string idxFileName = _subFileName.Substring(0, _subFileName.Length - 3) + "idx"; + File.WriteAllText(idxFileName, _idx.ToString().Trim()); + } + + private StringBuilder CreateIdxHeader() + { + var sb = new StringBuilder(); + sb.AppendLine(@"# VobSub index file, v7 (do not modify this line!) +# +# To repair desynchronization, you can insert gaps this way: +# (it usually happens after vob id changes) +# +# delay: [sign]hh:mm:ss:ms +# +# Where: +# [sign]: +, - (optional) +# hh: hours (0 <= hh) +# mm/ss: minutes/seconds (0 <= mm/ss <= 59) +# ms: milliseconds (0 <= ms <= 999) +# +# Note: You can't position a sub before the previous with a negative value. +# +# You can also modify timestamps or delete a few subs you don't like. +# Just make sure they stay in increasing order. + +# Settings + +# Original frame size +size: " + _screenWidth + "x" + _screenHeight + @" + +# Origin, relative to the upper-left corner, can be overloaded by aligment +org: 0, 0 + +# Image scaling (hor,ver), origin is at the upper-left corner or at the alignment coord (x, y) +scale: 100%, 100% + +# Alpha blending +alpha: 100% + +# Smoothing for very blocky images (use OLD for no filtering) +smooth: OFF + +# In milliseconds +fadein/out: 50, 50 + +# Force subtitle placement relative to (org.x, org.y) +align: OFF at LEFT TOP + +# For correcting non-progressive desync. (in milliseconds or hh:mm:ss:ms) +# Note: Not effective in DirectVobSub, use 'delay: ... ' instead. +time offset: 0 + +# ON: displays only forced subtitles, OFF: shows everything +forced subs: OFF + +# The original palette of the DVD +palette: 000000, " + ToHexColor(_pattern) + ", " + ToHexColor(_emphasis1) + ", " + ToHexColor(_emphasis2) + @", 828282, 828282, 828282, ffffff, 828282, bababa, 828282, 828282, 828282, 828282, 828282, 828282 + +# Custom colors (transp idxs and the four colors) +custom colors: OFF, tridx: 0000, colors: 000000, 000000, 000000, 000000 + +# Language index in use +langidx: 0 + +# " + _languageName + @" +id: " + _languageNameShort + @", index: 0 +# Decomment next line to activate alternative name in DirectVobSub / Windows Media Player 6.x +# alt: " + _languageName + @" +# Vob/Cell ID: 1, 1 (PTS: 0)"); + return sb; + } + + private static string ToHexColor(Color c) + { + return (c.R.ToString("X2") + c.G.ToString("X2") + c.B.ToString("X2")).ToLower(); + } + + private void ReleaseManagedResources() + { + if (_subFile != null) + { + _subFile.Dispose(); + _subFile = null; + } + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + ReleaseManagedResources(); + } + } + + } +} \ No newline at end of file diff --git a/libse/Wave.cs b/libse/Wave.cs new file mode 100644 index 000000000..dffd19690 --- /dev/null +++ b/libse/Wave.cs @@ -0,0 +1,18 @@ +namespace Nikse.SubtitleEdit.Core +{ +// class Wave +// { +// public Wave(string fileName) +// { +// RiffParser parser = new RiffParser(); +// RiffDecodeHeader decoder = new RiffDecodeHeader(parser); +// parser.OpenFile(fileName); +// if (RiffParser.ckidAVI == parser.FileType) +// { +// decoder.ProcessMainAVI(); +// decoder.ProcessMainWAVE(); +// parser.CloseFile(); +// } +// } +// } +} diff --git a/libse/WaveToVisualizer.cs b/libse/WaveToVisualizer.cs new file mode 100644 index 000000000..c9faa3dde --- /dev/null +++ b/libse/WaveToVisualizer.cs @@ -0,0 +1,638 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Globalization; +using System.IO; +using System.Text; +using System.Xml; + +namespace Nikse.SubtitleEdit.Core +{ + /// + /// http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html + /// + public class WaveHeader + { + private const int ConstantHeaderSize = 20; + private readonly byte[] _headerData; + + public string ChunkId { get; private set; } + public uint ChunkSize { get; private set; } + public string Format { get; private set; } + public string FmtId { get; private set; } + public int FmtChunkSize { get; private set; } + + /// + /// 1 = PCM (uncompressed) + /// 0x0101 = IBM mu-law format + /// 0x0102 = IBM a-law format + /// 0x0103 = IBM AVC Adaptive Differential Pulse Code Modulation format + /// 0xFFFE = WAVE_FORMAT_EXTENSIBLE, Determined by SubFormat + /// + public int AudioFormat { get; private set; } + + public int NumberOfChannels { get; private set; } + + /// + /// Number of samples per second + /// + public int SampleRate { get; private set; } + + /// + /// Should be SampleRate * BlockAlign + /// + public int ByteRate { get; private set; } + + /// + /// 8 bytes per block (32 bit); 6 bytes per block (24 bit); 4 bytes per block (16 bit) + /// + public int BlockAlign { get; private set; } + + public int BitsPerSample { get; private set; } + + public string DataId { get; private set; } + + /// + /// Size of sound data + /// + public uint DataChunkSize { get; private set; } + public int DataStartPosition { get; private set; } + + public WaveHeader(Stream stream) + { + stream.Position = 0; + var buffer = new byte[ConstantHeaderSize]; + int bytesRead = stream.Read(buffer, 0, buffer.Length); + if (bytesRead < buffer.Length) + throw new ArgumentException("Stream is too small"); + + // constant header + ChunkId = Encoding.UTF8.GetString(buffer, 0, 4); // Chunk ID: "RIFF" (Resource Interchange File Format), RF64 = new 64-bit format - see http://tech.ebu.ch/docs/tech/tech3306-2009.pdf + ChunkSize = BitConverter.ToUInt32(buffer, 4); // Chunk size: 16 or 18 or 40 + Format = Encoding.UTF8.GetString(buffer, 8, 4); // Format code - "WAVE" + FmtId = Encoding.UTF8.GetString(buffer, 12, 4); // Contains the letters "fmt " + FmtChunkSize = BitConverter.ToInt32(buffer, 16); // 16 for PCM. This is the size of the rest of the Subchunk which follows this number. + + // fmt data + buffer = new byte[FmtChunkSize]; + stream.Read(buffer, 0, buffer.Length); + AudioFormat = BitConverter.ToInt16(buffer, 0); // PCM = 1 + NumberOfChannels = BitConverter.ToInt16(buffer, 2); + SampleRate = BitConverter.ToInt32(buffer, 4); // 8000, 44100, etc. + ByteRate = BitConverter.ToInt32(buffer, 8); // SampleRate * NumChannels * BitsPerSample/8 + BlockAlign = BitConverter.ToInt16(buffer, 12); + BitsPerSample = BitConverter.ToInt16(buffer, 14); // 8 bits = 8, 16 bits = 16, etc. + + // data + buffer = new byte[8]; + stream.Position = ConstantHeaderSize + FmtChunkSize; + stream.Read(buffer, 0, buffer.Length); + DataId = Encoding.UTF8.GetString(buffer, 0, 4); + DataChunkSize = BitConverter.ToUInt32(buffer, 4); + DataStartPosition = ConstantHeaderSize + FmtChunkSize + 8; + + // if some other ChunckId than 'data' (e.g. LIST) we search for 'data' + long oldPos = ConstantHeaderSize + FmtChunkSize; + while (DataId != "data" && oldPos + DataChunkSize + 16 < stream.Length) + { + oldPos = oldPos + DataChunkSize + 8; + stream.Position = oldPos; + stream.Read(buffer, 0, buffer.Length); + DataId = Encoding.UTF8.GetString(buffer, 0, 4); + DataChunkSize = BitConverter.ToUInt32(buffer, 4); + DataStartPosition = (int)oldPos + 8; + } + + _headerData = new byte[DataStartPosition]; + stream.Position = 0; + stream.Read(_headerData, 0, _headerData.Length); + } + + public long BytesPerSecond + { + get + { + return (long)SampleRate * (BitsPerSample / 8) * NumberOfChannels; + } + } + + public double LengthInSeconds + { + get + { + return (double)DataChunkSize / BytesPerSecond; + } + } + + internal void WriteHeader(Stream toStream, int sampleRate, int numberOfChannels, int bitsPerSample, int dataSize) + { + const int fmtChunckSize = 16; + const int headerSize = 44; + int byteRate = sampleRate * (bitsPerSample / 8) * numberOfChannels; + WriteInt32ToByteArray(_headerData, 4, dataSize + headerSize - 8); + WriteInt16ToByteArray(_headerData, 16, fmtChunckSize); // + WriteInt16ToByteArray(_headerData, ConstantHeaderSize + 2, numberOfChannels); + WriteInt32ToByteArray(_headerData, ConstantHeaderSize + 4, sampleRate); + WriteInt32ToByteArray(_headerData, ConstantHeaderSize + 8, byteRate); + WriteInt16ToByteArray(_headerData, ConstantHeaderSize + 14, bitsPerSample); + _headerData[ConstantHeaderSize + fmtChunckSize + 0] = Convert.ToByte('d'); + _headerData[ConstantHeaderSize + fmtChunckSize + 1] = Convert.ToByte('a'); + _headerData[ConstantHeaderSize + fmtChunckSize + 2] = Convert.ToByte('t'); + _headerData[ConstantHeaderSize + fmtChunckSize + 3] = Convert.ToByte('a'); + WriteInt32ToByteArray(_headerData, ConstantHeaderSize + fmtChunckSize + 4, dataSize); + toStream.Write(_headerData, 0, headerSize); + } + + private static void WriteInt16ToByteArray(byte[] headerData, int index, int value) + { + byte[] buffer = BitConverter.GetBytes((short)value); + for (int i = 0; i < buffer.Length; i++) + headerData[index + i] = buffer[i]; + } + + private static void WriteInt32ToByteArray(byte[] headerData, int index, int value) + { + byte[] buffer = BitConverter.GetBytes(value); + for (int i = 0; i < buffer.Length; i++) + headerData[index + i] = buffer[i]; + } + } + + public class WavePeakGenerator + { + private Stream _stream; + private byte[] _data; + + private delegate int ReadSampleDataValueDelegate(ref int index); + + public WaveHeader Header { get; private set; } + + /// + /// Lowest data value + /// + public int DataMinValue { get; private set; } + + /// + /// Highest data value + /// + public int DataMaxValue { get; private set; } + + /// + /// Number of peaks per second (should be divideable by SampleRate) + /// + public int PeaksPerSecond { get; private set; } + + /// + /// List of all peak samples (channels are merged) + /// + public List PeakSamples { get; private set; } + + /// + /// List of all samples (channels are merged) + /// + public List AllSamples { get; private set; } + + /// + /// Constructor + /// + /// Wave file name + public WavePeakGenerator(string fileName) + { + Initialize(new FileStream(fileName, FileMode.Open, FileAccess.Read)); + } + + /// + /// Constructor + /// + /// Stream of a wave file + public WavePeakGenerator(Stream stream) + { + Initialize(stream); + } + + /// + /// Generate peaks (samples with some interval) for an uncompressed wave file + /// + /// Sampeles per second / sample rate + /// Delay in milliseconds (normally zero) + public void GeneratePeakSamples(int peaksPerSecond, int delayInMilliseconds) + { + PeaksPerSecond = peaksPerSecond; + + ReadSampleDataValueDelegate readSampleDataValue = GetSampleDataRerader(); + DataMinValue = int.MaxValue; + DataMaxValue = int.MinValue; + PeakSamples = new List(); + + if (delayInMilliseconds > 0) + { + for (int i = 0; i < peaksPerSecond * delayInMilliseconds / 1000; i++) + PeakSamples.Add(0); + } + + int bytesInterval = (int)Header.BytesPerSecond / PeaksPerSecond; + _data = new byte[Header.BytesPerSecond]; + _stream.Position = Header.DataStartPosition; + int bytesRead = _stream.Read(_data, 0, _data.Length); + while (bytesRead == Header.BytesPerSecond) + { + for (int i = 0; i < Header.BytesPerSecond; i += bytesInterval) + { + int index = i; + int value = 0; + for (int channelNumber = 0; channelNumber < Header.NumberOfChannels; channelNumber++) + { + value += readSampleDataValue.Invoke(ref index); + } + value = value / Header.NumberOfChannels; + if (value < DataMinValue) + DataMinValue = value; + if (value > DataMaxValue) + DataMaxValue = value; + PeakSamples.Add(value); + } + bytesRead = _stream.Read(_data, 0, _data.Length); + } + } + + public void GenerateAllSamples() + { + // determine how to read sample values + ReadSampleDataValueDelegate readSampleDataValue = GetSampleDataRerader(); + + // load data + _data = new byte[Header.DataChunkSize]; + _stream.Position = Header.DataStartPosition; + _stream.Read(_data, 0, _data.Length); + + // read sample values + DataMinValue = int.MaxValue; + DataMaxValue = int.MinValue; + AllSamples = new List(); + int index = 0; + while (index + Header.NumberOfChannels < Header.DataChunkSize) + { + int value = 0; + for (int channelNumber = 0; channelNumber < Header.NumberOfChannels; channelNumber++) + { + value += readSampleDataValue.Invoke(ref index); + } + value = value / Header.NumberOfChannels; + if (value < DataMinValue) + DataMinValue = value; + if (value > DataMaxValue) + DataMaxValue = value; + AllSamples.Add(value); + } + } + + public void WritePeakSamples(string fileName) + { + using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write)) + { + WritePeakSamples(fs); + } + } + + public void WritePeakSamples(Stream stream) + { + Header.WriteHeader(stream, PeaksPerSecond, 1, 16, PeakSamples.Count * 2); + WritePeakData(stream); + stream.Flush(); + } + + private void WritePeakData(Stream stream) + { + foreach (var value in PeakSamples) + { + byte[] buffer = BitConverter.GetBytes((short)(value)); + stream.Write(buffer, 0, buffer.Length); + } + } + + private void Initialize(Stream stream) + { + _stream = stream; + Header = new WaveHeader(_stream); + } + + private int ReadValue8Bit(ref int index) + { + int result = _data[index]; + index += 2; + return result; + } + + private int ReadValue16Bit(ref int index) + { + int result = BitConverter.ToInt16(_data, index); + index += 2; + return result; + } + + private int ReadValue24Bit(ref int index) + { + var buffer = new byte[4]; + buffer[0] = 0; + buffer[1] = _data[index]; + buffer[2] = _data[index + 1]; + buffer[3] = _data[index + 2]; + int result = BitConverter.ToInt32(buffer, 0); + index += 3; + return result; + } + + private int ReadValue32Bit(ref int index) + { + int result = BitConverter.ToInt32(_data, index); + index += 4; + return result; + } + + /// + /// Determine how to read sample values + /// + /// Sample data reader that matches bits per sample + private ReadSampleDataValueDelegate GetSampleDataRerader() + { + ReadSampleDataValueDelegate readSampleDataValue; + switch (Header.BitsPerSample) + { + case 8: + readSampleDataValue = ReadValue8Bit; + break; + case 16: + readSampleDataValue = ReadValue16Bit; + break; + case 24: + readSampleDataValue = ReadValue24Bit; + break; + case 32: + readSampleDataValue = ReadValue32Bit; + break; + default: + throw new InvalidDataException("Cannot read bits per sample of " + Header.BitsPerSample); + } + return readSampleDataValue; + } + + public void Dispose() + { + Close(); + } + + public void Close() + { + if (_stream != null) + _stream.Close(); + } + + //////////////////////////////////////// SPECTRUM /////////////////////////////////////////////////////////// + + public List GenerateFourierData(int nfft, string spectrogramDirectory, int delayInMilliseconds) + { + const int bitmapWidth = 1024; + var bitmaps = new List(); + + // setup fourier transformation + var f = new Fourier(nfft, true); + double divider = 2.0; + for (int k = 0; k < Header.BitsPerSample - 2; k++) + divider *= 2; + + // determine how to read sample values + ReadSampleDataValueDelegate readSampleDataValue = GetSampleDataRerader(); + + // set up one column of the spectrogram + var palette = new Color[nfft]; + if (Configuration.Settings.VideoControls.SpectrogramAppearance == "Classic") + { + for (int colorIndex = 0; colorIndex < nfft; colorIndex++) + palette[colorIndex] = PaletteValue(colorIndex, nfft); + } + else + { + var list = SmoothColors(0, 0, 0, Configuration.Settings.VideoControls.WaveformColor.R, + Configuration.Settings.VideoControls.WaveformColor.G, + Configuration.Settings.VideoControls.WaveformColor.B, nfft); + for (int i = 0; i < nfft; i++) + palette[i] = list[i]; + } + + // read sample values + DataMinValue = int.MaxValue; + DataMaxValue = int.MinValue; + var samples = new List(); + int index = 0; + int sampleSize = nfft * bitmapWidth; + int count = 0; + long totalSamples = 0; + + // write delay (if any) + int delaySampleCount = (int)(Header.SampleRate * (delayInMilliseconds / TimeCode.BaseUnit)); + for (int i = 0; i < delaySampleCount; i++) + { + samples.Add(0); + if (samples.Count == sampleSize) + { + var samplesAsReal = new double[sampleSize]; + for (int k = 0; k < sampleSize; k++) + samplesAsReal[k] = 0; + var bmp = DrawSpectrogram(nfft, samplesAsReal, f, palette); + bmp.Save(Path.Combine(spectrogramDirectory, count + ".gif"), System.Drawing.Imaging.ImageFormat.Gif); + bitmaps.Add(bmp); + samples = new List(); + count++; + } + } + + // load data in smaller parts + _data = new byte[Header.BytesPerSecond]; + _stream.Position = Header.DataStartPosition; + int bytesRead = _stream.Read(_data, 0, _data.Length); + while (bytesRead == Header.BytesPerSecond) + { + while (index < Header.BytesPerSecond) + { + int value = 0; + for (int channelNumber = 0; channelNumber < Header.NumberOfChannels; channelNumber++) + { + value += readSampleDataValue.Invoke(ref index); + } + value = value / Header.NumberOfChannels; + if (value < DataMinValue) + DataMinValue = value; + if (value > DataMaxValue) + DataMaxValue = value; + samples.Add(value); + totalSamples++; + + if (samples.Count == sampleSize) + { + var samplesAsReal = new double[sampleSize]; + for (int k = 0; k < sampleSize; k++) + samplesAsReal[k] = samples[k] / divider; + var bmp = DrawSpectrogram(nfft, samplesAsReal, f, palette); + bmp.Save(Path.Combine(spectrogramDirectory, count + ".gif"), System.Drawing.Imaging.ImageFormat.Gif); + bitmaps.Add(bmp); + samples = new List(); + count++; + } + } + bytesRead = _stream.Read(_data, 0, _data.Length); + index = 0; + } + + if (samples.Count > 0) + { + var samplesAsReal = new double[sampleSize]; + for (int k = 0; k < sampleSize && k < samples.Count; k++) + samplesAsReal[k] = samples[k] / divider; + var bmp = DrawSpectrogram(nfft, samplesAsReal, f, palette); + bmp.Save(Path.Combine(spectrogramDirectory, count + ".gif"), System.Drawing.Imaging.ImageFormat.Gif); + bitmaps.Add(bmp); + } + + var doc = new XmlDocument(); + doc.LoadXml(""); + double sampleDuration = Header.LengthInSeconds / (totalSamples / Convert.ToDouble(nfft)); + doc.DocumentElement.SelectSingleNode("SampleDuration").InnerText = sampleDuration.ToString(CultureInfo.InvariantCulture); + doc.DocumentElement.SelectSingleNode("TotalDuration").InnerText = Header.LengthInSeconds.ToString(CultureInfo.InvariantCulture); + doc.DocumentElement.SelectSingleNode("AudioFormat").InnerText = Header.AudioFormat.ToString(CultureInfo.InvariantCulture); + doc.DocumentElement.SelectSingleNode("ChunkId").InnerText = Header.ChunkId.ToString(CultureInfo.InvariantCulture); + doc.DocumentElement.SelectSingleNode("SecondsPerImage").InnerText = ((double)(sampleSize / (double)Header.SampleRate)).ToString(CultureInfo.InvariantCulture); + doc.DocumentElement.SelectSingleNode("ImageWidth").InnerText = bitmapWidth.ToString(CultureInfo.InvariantCulture); + doc.DocumentElement.SelectSingleNode("NFFT").InnerText = nfft.ToString(CultureInfo.InvariantCulture); + doc.Save(Path.Combine(spectrogramDirectory, "Info.xml")); + + return bitmaps; + } + + private static Bitmap DrawSpectrogram(int nfft, double[] samples, Fourier f, Color[] palette) + { + const int overlap = 0; + int numSamples = samples.Length; + int colIncrement = nfft * (1 - overlap); + + int numcols = numSamples / colIncrement; + // make sure we don't step beyond the end of the recording + while ((numcols - 1) * colIncrement + nfft > numSamples) + numcols--; + + double[] real = new double[nfft]; + double[] imag = new double[nfft]; + double[] magnitude = new double[nfft / 2]; + var bmp = new Bitmap(numcols, nfft / 2); + for (int col = 0; col <= numcols - 1; col++) + { + // read a segment of the recorded signal + for (int c = 0; c <= nfft - 1; c++) + { + imag[c] = 0; + real[c] = samples[col * colIncrement + c] * Fourier.Hanning(nfft, c); + } + + // transform to the frequency domain + f.FourierTransform(real, imag); + + // and compute the magnitude spectrum + f.MagnitudeSpectrum(real, imag, Fourier.W0Hanning, magnitude); + + // Draw + for (int newY = 0; newY < nfft / 2 - 1; newY++) + { + int colorIndex = MapToPixelIndex(magnitude[newY], 100, 255); + bmp.SetPixel(col, (nfft / 2 - 1) - newY, palette[colorIndex]); + } + } + return bmp; + } + + public static Color PaletteValue(int x, int range) + { + double g; + double r; + double b; + + double r4 = range / 4.0; + const double u = 255; + + if (x < r4) + { + b = x / r4; + g = 0; + r = 0; + } + else if (x < 2 * r4) + { + b = (1 - (x - r4) / r4); + g = 1 - b; + r = 0; + } + else if (x < 3 * r4) + { + b = 0; + g = (2 - (x - r4) / r4); + r = 1 - g; + } + else + { + b = (x - 3 * r4) / r4; + g = 0; + r = 1 - b; + } + + r = ((int)(Math.Sqrt(r) * u)) & 0xff; + g = ((int)(Math.Sqrt(g) * u)) & 0xff; + b = ((int)(Math.Sqrt(b) * u)) & 0xff; + + return Color.FromArgb((int)r, (int)g, (int)b); + } + + /// + /// Maps magnitudes in the range [-rangedB .. 0] dB to palette index values in the range [0 .. rangeIndex-1] + /// and computes and returns the index value which corresponds to passed-in magnitude + /// + private static int MapToPixelIndex(double magnitude, double rangedB, int rangeIndex) + { + const double log10 = 2.30258509299405; + + if (magnitude == 0) + return 0; + + double levelIndB = 20 * Math.Log(magnitude) / log10; + if (levelIndB < -rangedB) + return 0; + + return (int)(rangeIndex * (levelIndB + rangedB) / rangedB); + } + + private static List SmoothColors(int fromR, int fromG, int fromB, int toR, int toG, int toB, int count) + { + while (toR < 255 && toG < 255 && toB < 255) + { + toR++; + toG++; + toB++; + } + + var list = new List(); + double r = fromR; + double g = fromG; + double b = fromB; + double diffR = (toR - fromR) / (double)count; + double diffG = (toG - fromG) / (double)count; + double diffB = (toB - fromB) / (double)count; + + for (int i = 0; i < count; i++) + { + list.Add(Color.FromArgb((int)r, (int)g, (int)b)); + r += diffR; + g += diffG; + b += diffB; + } + return list; + } + + } +} \ No newline at end of file diff --git a/libse/XSub.cs b/libse/XSub.cs new file mode 100644 index 000000000..3039bfc16 --- /dev/null +++ b/libse/XSub.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using System.Drawing; + +namespace Nikse.SubtitleEdit.Core +{ + public class XSub + { + public TimeCode Start { get; set; } + public TimeCode End { get; set; } + public int Width { get; private set; } + public int Height { get; private set; } + + private byte[] colorBuffer; + private byte[] rleBuffer; + + public XSub(string timeCode, int width, int height, byte[] colors, byte[] rle) + { + Start = DecodeTimeCode(timeCode.Substring(0, 13)); + End = DecodeTimeCode(timeCode.Substring(13, 12)); + Width = width; + Height = height; + colorBuffer = colors; + rleBuffer = rle; + } + + private static TimeCode DecodeTimeCode(string timeCode) + { + var parts = timeCode.Split(new[] { ':', ';', '.', ',', '-' }, StringSplitOptions.RemoveEmptyEntries); + return new TimeCode(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), int.Parse(parts[3])); + } + + private static int GenerateBitmap(FastBitmap bmp, byte[] buf, List fourColors) + { + int w = bmp.Width; + int h = bmp.Height; + int nibbleOffset = 0; + var nibble_end = buf.Length * 2; + var x = 0; + var y = 0; + for (; ; ) + { + if (nibbleOffset >= nibble_end) + return -1; + var v = GetNibble(buf, nibbleOffset++); + if (v < 0x4) + { + v = (v << 4) | GetNibble(buf, nibbleOffset++); + if (v < 0x10) + { + v = (v << 4) | GetNibble(buf, nibbleOffset++); + if (v < 0x040) + { + v = (v << 4) | GetNibble(buf, nibbleOffset++); + if (v < 4) + { + v |= (w - x) << 2; + } + } + } + } + + var len = v >> 2; + if (len > (w - x)) + len = (w - x); + + var color = v & 0x03; + if (color > 0) + { + Color c = fourColors[color]; + bmp.SetPixel(x, y, c, len); + } + + x += len; + if (x >= w) + { + y++; + if (y >= h) + break; + x = 0; + nibbleOffset += (nibbleOffset & 1); + } + } + return 0; + } + + private static int GetNibble(byte[] buf, int nibble_offset) + { + return (buf[nibble_offset >> 1] >> ((1 - (nibble_offset & 1)) << 2)) & 0xf; + } + + public Bitmap GetImage(Color background, Color pattern, Color emphasis1, Color emphasis2) + { + var fourColors = new List { background, pattern, emphasis1, emphasis2 }; + var bmp = new Bitmap(Width, Height); + if (fourColors[0] != Color.Transparent) + { + using (var gr = Graphics.FromImage(bmp)) + { + gr.FillRectangle(new SolidBrush(fourColors[0]), new Rectangle(0, 0, bmp.Width, bmp.Height)); + } + } + var fastBmp = new FastBitmap(bmp); + fastBmp.LockImage(); + GenerateBitmap(fastBmp, rleBuffer, fourColors); + fastBmp.UnlockImage(); + return bmp; + } + + private Color GetColor(int start) + { + return Color.FromArgb(colorBuffer[start], colorBuffer[start + 1], colorBuffer[start + 2]); + } + + public Bitmap GetImage() + { + return GetImage(Color.Transparent, GetColor(3), GetColor(6), GetColor(9)); + } + + } +} diff --git a/libse/XmlDeserializerGenerator.cs b/libse/XmlDeserializerGenerator.cs new file mode 100644 index 000000000..b3eadbc01 --- /dev/null +++ b/libse/XmlDeserializerGenerator.cs @@ -0,0 +1,105 @@ +using System; +using System.Text; + +namespace Nikse.SubtitleEdit.Core +{ + public static class XmlDeserializerGenerator + { + + public static string GenerateCSharpXmlDeserializerForLanguageStructure() + { + var sb = new StringBuilder(); + sb.AppendLine(@"using System.IO; +using System.Xml; + +namespace Nikse.SubtitleEdit.Logic +{ + + public class LanguageDeserializer + { + + public static Language CustomDeserializeLanguage(string fileName) + { + var doc = new XmlDocument(); + doc.PreserveWhitespace = true; + + var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + doc.Load(stream); + stream.Close(); + + XmlNode node = doc.DocumentElement; + XmlNode subNode; + var language = new Language(); +"); + sb.AppendLine(GenerateCSharpXmlDeserializer(typeof(Language), "language", string.Empty)); + sb.AppendLine(); + sb.AppendLine("\t\t\treturn language;"); + sb.AppendLine("\t\t}"); + sb.AppendLine("\t}"); + sb.AppendLine("}"); + return sb.ToString(); + } + + private static string GenerateCSharpXmlDeserializer(Type classType, string currentName, string xmlPath) + { + xmlPath = xmlPath.Trim('/'); + + var sb = new StringBuilder(); + + var properties = classType.GetProperties(); + if (properties.Length == 0) + { + var fields = classType.GetFields(); + foreach (var fieldInfo in fields) + { + if (fieldInfo.FieldType.Name == "String") + { + sb.AppendLine("\t\t\tsubNode = node.SelectSingleNode(\"" + fieldInfo.Name + "\");"); + sb.AppendLine("\t\t\tif (subNode != null)"); + sb.AppendLine("\t\t\t\t" + currentName + "." + fieldInfo.Name + " = subNode.InnerText;"); + } + } + foreach (var fieldInfo in fields) + { + if (fieldInfo.FieldType.Name != "String" && fieldInfo.FieldType.FullName.Contains("LanguageStructure")) + { + sb.AppendLine(); + sb.AppendLine("\t\t\t" + currentName + "." + fieldInfo.Name + " = new " + fieldInfo.FieldType.FullName.Replace("+", ".") + "();"); + sb.AppendLine("\t\t\tnode = doc.DocumentElement.SelectSingleNode(\"" + fieldInfo.Name + "\");"); + sb.AppendLine("\t\t\tif (node != null)"); + sb.AppendLine("\t\t\t{"); + sb.AppendLine(GenerateCSharpXmlDeserializer(fieldInfo.FieldType, currentName + "." + fieldInfo.Name, xmlPath + "/" + fieldInfo.Name + "/")); + sb.AppendLine("\t\t\t}"); + } + } + } + else + { + foreach (var prp in properties) + { + if (prp.PropertyType.Name == "String") + { + sb.AppendLine("\t\t\tsubNode = node.SelectSingleNode(\"" + prp.Name + "\");"); + sb.AppendLine("\t\t\tif (subNode != null)"); + sb.AppendLine("\t\t\t\t" + currentName + "." + prp.Name + " = subNode.InnerText;"); + } + } + foreach (var prp in properties) + { + if (prp.PropertyType.Name != "String" && prp.PropertyType.FullName.Contains("LanguageStructure")) + { + sb.AppendLine(); + sb.AppendLine("\t\t\t" + currentName + "." + prp.Name + " = new " + prp.PropertyType.FullName.Replace("+", ".") + "();"); + sb.AppendLine("\t\t\tnode = doc.DocumentElement.SelectSingleNode(\"" + xmlPath + "/" + prp.Name + "\");"); + sb.AppendLine("\t\t\tif (node != null)"); + sb.AppendLine("\t\t\t{"); + sb.AppendLine(GenerateCSharpXmlDeserializer(prp.PropertyType, currentName + "." + prp.Name, xmlPath + "/" + prp.Name + "/")); + sb.AppendLine("\t\t\t}"); + } + } + } + return sb.ToString(); + } + + } +} diff --git a/libse/ZipExtractor.cs b/libse/ZipExtractor.cs new file mode 100644 index 000000000..eedceaa44 --- /dev/null +++ b/libse/ZipExtractor.cs @@ -0,0 +1,440 @@ +// ZipStorer, by Jaime Olivares +// Website: zipstorer.codeplex.com +// Version: 2.35 (March 14, 2010) + +// Simplified to extract-only by Nikse - August 18, 2010 + +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Text; + +namespace Nikse.SubtitleEdit.Core +{ + /// + /// Zip archive decompression. Represents a Zip file. + /// + public class ZipExtractor : IDisposable + { + /// + /// Compression method enumeration + /// + public enum Compression : ushort + { + /// Uncompressed storage + Store = 0, + /// Deflate compression method + Deflate = 8 + } + + /// + /// Represents an entry in Zip file directory + /// + public struct ZipFileEntry + { + /// Compression method + public Compression Method; + /// Full path and filename as stored in Zip + public string FilenameInZip; + /// Original file size + public uint FileSize; + /// Compressed file size + public uint CompressedSize; + /// Offset of header information inside Zip storage + public uint HeaderOffset; + /// Offset of file inside Zip storage + public uint FileOffset; + /// Size of header information + public uint HeaderSize; + /// 32-bit checksum of entire file + public uint Crc32; + /// Last modification time of file + public DateTime ModifyTime; + /// User comment for file + public string Comment; + /// True if UTF8 encoding for filename and comments, false if default (CP 437) + public bool EncodeUTF8; + + /// Overriden method + /// Filename in Zip + public override string ToString() + { + return this.FilenameInZip; + } + } + + #region Public fields + + /// True if UTF8 encoding for filename and comments, false if default (CP 437) + public bool EncodeUTF8 = false; + /// Force deflate algotithm even if it inflates the stored file. Off by default. + public bool ForceDeflating = false; + + #endregion Public fields + + #region Private fields + + // Stream object of storage file + private Stream ZipFileStream; + // Central dir image + private byte[] CentralDirImage = null; + // Static CRC32 Table + private static UInt32[] CrcTable = null; + // Default filename encoder + private static Encoding DefaultEncoding = Encoding.GetEncoding(437); + + #endregion Private fields + + #region Public methods + + // Static constructor. Just invoked once in order to create the CRC32 lookup table. + static ZipExtractor() + { + // Generate CRC32 table + CrcTable = new UInt32[256]; + for (int i = 0; i < CrcTable.Length; i++) + { + UInt32 c = (UInt32)i; + for (int j = 0; j < 8; j++) + { + if ((c & 1) != 0) + c = 3988292384 ^ (c >> 1); + else + c >>= 1; + } + CrcTable[i] = c; + } + } + + /// + /// Method to open an existing storage file + /// + /// Full path of Zip file to open + /// A valid ZipStorer object + public static ZipExtractor Open(string filename) + { + Stream stream = new FileStream(filename, FileMode.Open, FileAccess.Read); + ZipExtractor zip = Open(stream); + return zip; + } + + /// + /// Method to open an existing storage from stream + /// + /// Already opened stream with zip contents + /// A valid ZipStorer object + public static ZipExtractor Open(Stream stream) + { + ZipExtractor zip = new ZipExtractor(); + zip.ZipFileStream = stream; + + if (zip.ReadFileInfo()) + return zip; + + throw new System.IO.InvalidDataException(); + } + + /// + /// Close the Zip storage + /// + /// This is a required step, unless automatic dispose is used + public void Close() + { + if (this.ZipFileStream != null) + { + this.ZipFileStream.Flush(); + this.ZipFileStream.Dispose(); + this.ZipFileStream = null; + } + } + + /// + /// Read all the file records in the central directory + /// + /// List of all entries in directory + public List ReadCentralDir() + { + if (this.CentralDirImage == null) + throw new InvalidOperationException("Central directory currently does not exist"); + + List result = new List(); + + for (int pointer = 0; pointer < this.CentralDirImage.Length; ) + { + uint signature = BitConverter.ToUInt32(CentralDirImage, pointer); + if (signature != 0x02014b50) + break; + + bool encodeUTF8 = (BitConverter.ToUInt16(CentralDirImage, pointer + 8) & 0x0800) != 0; + ushort method = BitConverter.ToUInt16(CentralDirImage, pointer + 10); + uint modifyTime = BitConverter.ToUInt32(CentralDirImage, pointer + 12); + uint crc32 = BitConverter.ToUInt32(CentralDirImage, pointer + 16); + uint comprSize = BitConverter.ToUInt32(CentralDirImage, pointer + 20); + uint fileSize = BitConverter.ToUInt32(CentralDirImage, pointer + 24); + ushort filenameSize = BitConverter.ToUInt16(CentralDirImage, pointer + 28); + ushort extraSize = BitConverter.ToUInt16(CentralDirImage, pointer + 30); + ushort commentSize = BitConverter.ToUInt16(CentralDirImage, pointer + 32); + uint headerOffset = BitConverter.ToUInt32(CentralDirImage, pointer + 42); + uint headerSize = (uint)(46 + filenameSize + extraSize + commentSize); + + Encoding encoder = encodeUTF8 ? Encoding.UTF8 : DefaultEncoding; + + ZipFileEntry zfe = new ZipFileEntry(); + zfe.Method = (Compression)method; + zfe.FilenameInZip = encoder.GetString(CentralDirImage, pointer + 46, filenameSize); + zfe.FileOffset = GetFileOffset(headerOffset); + zfe.FileSize = fileSize; + zfe.CompressedSize = comprSize; + zfe.HeaderOffset = headerOffset; + zfe.HeaderSize = headerSize; + zfe.Crc32 = crc32; + zfe.ModifyTime = DosTimeToDateTime(modifyTime); + if (commentSize > 0) + zfe.Comment = encoder.GetString(CentralDirImage, pointer + 46 + filenameSize + extraSize, commentSize); + + result.Add(zfe); + pointer += (46 + filenameSize + extraSize + commentSize); + } + + return result; + } + + /// + /// Copy the contents of a stored file into a physical file + /// + /// Entry information of file to extract + /// Name of file to store uncompressed data + /// True if success, false if not. + /// Unique compression methods are Store and Deflate + public bool ExtractFile(ZipFileEntry zfe, string filename) + { + // Make sure the parent directory exist + string path = System.IO.Path.GetDirectoryName(filename); + if (!Directory.Exists(path)) + Directory.CreateDirectory(path); + + // Check it is directory. If so, do nothing + if (Directory.Exists(filename)) + return true; + + bool result = false; + using (Stream output = new FileStream(filename, FileMode.Create, FileAccess.Write)) + { + result = ExtractFile(zfe, output); + } + File.SetCreationTime(filename, zfe.ModifyTime); + File.SetLastWriteTime(filename, zfe.ModifyTime); + return result; + } + + /// + /// Copy the contents of a stored file into an opened stream + /// + /// Entry information of file to extract + /// Stream to store the uncompressed data + /// True if success, false if not. + /// Unique compression methods are Store and Deflate + public bool ExtractFile(ZipFileEntry zfe, Stream stream) + { + if (!stream.CanWrite) + throw new InvalidOperationException("Stream cannot be written"); + + // check signature + byte[] signature = new byte[4]; + this.ZipFileStream.Seek(zfe.HeaderOffset, SeekOrigin.Begin); + this.ZipFileStream.Read(signature, 0, 4); + if (BitConverter.ToUInt32(signature, 0) != 0x04034b50) + return false; + + // Select input stream for inflating or just reading + Stream inStream; + if (zfe.Method == Compression.Store) + inStream = this.ZipFileStream; + else if (zfe.Method == Compression.Deflate) + inStream = new DeflateStream(this.ZipFileStream, CompressionMode.Decompress, true); + else + return false; + + // Buffered copy + byte[] buffer = new byte[16384]; + this.ZipFileStream.Seek(zfe.FileOffset, SeekOrigin.Begin); + uint bytesPending = zfe.FileSize; + while (bytesPending > 0) + { + int bytesRead = inStream.Read(buffer, 0, (int)Math.Min(bytesPending, buffer.Length)); + stream.Write(buffer, 0, bytesRead); + bytesPending -= (uint)bytesRead; + } + stream.Flush(); + + if (zfe.Method == Compression.Deflate) + inStream.Dispose(); + return true; + } + + #endregion Public methods + + #region Private methods + + /// + /// Calculate the file offset by reading the corresponding local header + /// + private uint GetFileOffset(uint headerOffset) + { + byte[] buffer = new byte[2]; + + this.ZipFileStream.Seek(headerOffset + 26, SeekOrigin.Begin); + this.ZipFileStream.Read(buffer, 0, 2); + ushort filenameSize = BitConverter.ToUInt16(buffer, 0); + this.ZipFileStream.Read(buffer, 0, 2); + ushort extraSize = BitConverter.ToUInt16(buffer, 0); + + return (uint)(30 + filenameSize + extraSize + headerOffset); + } + /* Local file header: + local file header signature 4 bytes (0x04034b50) + version needed to extract 2 bytes + general purpose bit flag 2 bytes + compression method 2 bytes + last mod file time 2 bytes + last mod file date 2 bytes + crc-32 4 bytes + compressed size 4 bytes + uncompressed size 4 bytes + filename length 2 bytes + extra field length 2 bytes + + filename (variable size) + extra field (variable size) + */ + + /* Central directory's File header: + central file header signature 4 bytes (0x02014b50) + version made by 2 bytes + version needed to extract 2 bytes + general purpose bit flag 2 bytes + compression method 2 bytes + last mod file time 2 bytes + last mod file date 2 bytes + crc-32 4 bytes + compressed size 4 bytes + uncompressed size 4 bytes + filename length 2 bytes + extra field length 2 bytes + file comment length 2 bytes + disk number start 2 bytes + internal file attributes 2 bytes + external file attributes 4 bytes + relative offset of local header 4 bytes + + filename (variable size) + extra field (variable size) + file comment (variable size) + + * + /* End of central dir record: + end of central dir signature 4 bytes (0x06054b50) + number of this disk 2 bytes + number of the disk with the + start of the central directory 2 bytes + total number of entries in + the central dir on this disk 2 bytes + total number of entries in + the central dir 2 bytes + size of the central directory 4 bytes + offset of start of central + directory with respect to + the starting disk number 4 bytes + zipfile comment length 2 bytes + zipfile comment (variable size) + */ + + /* DOS Date and time: + MS-DOS date. The date is a packed value with the following format. Bits Description + 0-4 Day of the month (1–31) + 5-8 Month (1 = January, 2 = February, and so on) + 9-15 Year offset from 1980 (add 1980 to get actual year) + MS-DOS time. The time is a packed value with the following format. Bits Description + 0-4 Second divided by 2 + 5-10 Minute (0–59) + 11-15 Hour (0–23 on a 24-hour clock) + */ + + private static DateTime DosTimeToDateTime(uint dt) + { + return new DateTime( + (int)(dt >> 25) + 1980, + (int)(dt >> 21) & 15, + (int)(dt >> 16) & 31, + (int)(dt >> 11) & 31, + (int)(dt >> 5) & 63, + (int)(dt & 31) * 2); + } + + // Reads the end-of-central-directory record + private bool ReadFileInfo() + { + if (this.ZipFileStream.Length < 22) + return false; + + try + { + this.ZipFileStream.Seek(-17, SeekOrigin.End); + BinaryReader br = new BinaryReader(this.ZipFileStream); + do + { + this.ZipFileStream.Seek(-5, SeekOrigin.Current); + UInt32 sig = br.ReadUInt32(); + if (sig == 0x06054b50) + { + this.ZipFileStream.Seek(6, SeekOrigin.Current); + + UInt16 entries = br.ReadUInt16(); + Int32 centralSize = br.ReadInt32(); + UInt32 centralDirOffset = br.ReadUInt32(); + UInt16 commentSize = br.ReadUInt16(); + + // check if comment field is the very last data in file + if (this.ZipFileStream.Position + commentSize != this.ZipFileStream.Length) + return false; + + // Copy entire central directory to a memory buffer + this.CentralDirImage = new byte[centralSize]; + this.ZipFileStream.Seek(centralDirOffset, SeekOrigin.Begin); + this.ZipFileStream.Read(this.CentralDirImage, 0, centralSize); + + // Leave the pointer at the begining of central dir, to append new files + this.ZipFileStream.Seek(centralDirOffset, SeekOrigin.Begin); + return true; + } + } while (this.ZipFileStream.Position > 0); + } + catch + { + } + + return false; + } + + #endregion Private methods + + #region IDisposable Members + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + this.Close(); + } + } + + #endregion IDisposable Members + } +} \ No newline at end of file diff --git a/src/Controls/AudioVisualizer.cs b/src/Controls/AudioVisualizer.cs index 66631f55c..4f1b6a10b 100644 --- a/src/Controls/AudioVisualizer.cs +++ b/src/Controls/AudioVisualizer.cs @@ -1,4 +1,5 @@ -using Nikse.SubtitleEdit.Core; +using Nikse.Core; +using Nikse.SubtitleEdit.Core; using Nikse.SubtitleEdit.Logic; using System; using System.Collections.Generic; diff --git a/src/Controls/SubtitleListView.cs b/src/Controls/SubtitleListView.cs index 29838a138..251071974 100644 --- a/src/Controls/SubtitleListView.cs +++ b/src/Controls/SubtitleListView.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Drawing; using System.Globalization; using System.Windows.Forms; +using Nikse.Core; using Nikse.SubtitleEdit.Logic; using Nikse.SubtitleEdit.Core; @@ -618,7 +619,7 @@ namespace Nikse.SubtitleEdit.Controls subItem = new ListViewItem.ListViewSubItem(item, paragraph.EndTime.ToHHMMSSFF()); item.SubItems.Add(subItem); - subItem = new ListViewItem.ListViewSubItem(item, string.Format("{0},{1:00}", paragraph.Duration.Seconds, Logic.SubtitleFormats.SubtitleFormat.MillisecondsToFramesMaxFrameRate(paragraph.Duration.Milliseconds))); + subItem = new ListViewItem.ListViewSubItem(item, string.Format("{0},{1:00}", paragraph.Duration.Seconds, Core.SubtitleFormats.SubtitleFormat.MillisecondsToFramesMaxFrameRate(paragraph.Duration.Milliseconds))); item.SubItems.Add(subItem); } else @@ -768,7 +769,7 @@ namespace Nikse.SubtitleEdit.Controls else item.SubItems[ColumnIndexEnd].Text = paragraph.EndTime.ToHHMMSSFF(); - item.SubItems[ColumnIndexDuration].Text = string.Format("{0},{1:00}", paragraph.Duration.Seconds, Logic.SubtitleFormats.SubtitleFormat.MillisecondsToFramesMaxFrameRate(paragraph.Duration.Milliseconds)); + item.SubItems[ColumnIndexDuration].Text = string.Format("{0},{1:00}", paragraph.Duration.Seconds, Nikse.SubtitleEdit.Core.SubtitleFormats.SubtitleFormat.MillisecondsToFramesMaxFrameRate(paragraph.Duration.Milliseconds)); } else { @@ -891,7 +892,7 @@ namespace Nikse.SubtitleEdit.Controls ListViewItem item = Items[index]; if (Configuration.Settings != null && Configuration.Settings.General.UseTimeFormatHHMMSSFF) { - item.SubItems[ColumnIndexDuration].Text = string.Format("{0},{1:00}", paragraph.Duration.Seconds, Logic.SubtitleFormats.SubtitleFormat.MillisecondsToFramesMaxFrameRate(paragraph.Duration.Milliseconds)); + item.SubItems[ColumnIndexDuration].Text = string.Format("{0},{1:00}", paragraph.Duration.Seconds, Nikse.SubtitleEdit.Core.SubtitleFormats.SubtitleFormat.MillisecondsToFramesMaxFrameRate(paragraph.Duration.Milliseconds)); if (paragraph.EndTime.IsMaxTime) item.SubItems[ColumnIndexEnd].Text = "-"; else @@ -930,7 +931,7 @@ namespace Nikse.SubtitleEdit.Controls Paragraph p = subtitle.Paragraphs[i]; item.SubItems[ColumnIndexStart].Text = p.StartTime.ToHHMMSSFF(); item.SubItems[ColumnIndexEnd].Text = p.EndTime.ToHHMMSSFF(); - item.SubItems[ColumnIndexDuration].Text = string.Format("{0},{1:00}", p.Duration.Seconds, Logic.SubtitleFormats.SubtitleFormat.MillisecondsToFramesMaxFrameRate(p.Duration.Milliseconds)); + item.SubItems[ColumnIndexDuration].Text = string.Format("{0},{1:00}", p.Duration.Seconds, Core.SubtitleFormats.SubtitleFormat.MillisecondsToFramesMaxFrameRate(p.Duration.Milliseconds)); } } EndUpdate(); @@ -954,7 +955,7 @@ namespace Nikse.SubtitleEdit.Controls else item.SubItems[ColumnIndexEnd].Text = paragraph.EndTime.ToHHMMSSFF(); - item.SubItems[ColumnIndexDuration].Text = string.Format("{0},{1:00}", paragraph.Duration.Seconds, Logic.SubtitleFormats.SubtitleFormat.MillisecondsToFramesMaxFrameRate(paragraph.Duration.Milliseconds)); + item.SubItems[ColumnIndexDuration].Text = string.Format("{0},{1:00}", paragraph.Duration.Seconds, Core.SubtitleFormats.SubtitleFormat.MillisecondsToFramesMaxFrameRate(paragraph.Duration.Milliseconds)); } else { @@ -990,7 +991,7 @@ namespace Nikse.SubtitleEdit.Controls else item.SubItems[ColumnIndexEnd].Text = paragraph.EndTime.ToHHMMSSFF(); - item.SubItems[ColumnIndexDuration].Text = string.Format("{0},{1:00}", paragraph.Duration.Seconds, Logic.SubtitleFormats.SubtitleFormat.MillisecondsToFramesMaxFrameRate(paragraph.Duration.Milliseconds)); + item.SubItems[ColumnIndexDuration].Text = string.Format("{0},{1:00}", paragraph.Duration.Seconds, Core.SubtitleFormats.SubtitleFormat.MillisecondsToFramesMaxFrameRate(paragraph.Duration.Milliseconds)); } else { diff --git a/src/Controls/TimeUpDown.cs b/src/Controls/TimeUpDown.cs index 6ee22f8ea..d6fc364c9 100644 --- a/src/Controls/TimeUpDown.cs +++ b/src/Controls/TimeUpDown.cs @@ -1,4 +1,5 @@ -using Nikse.SubtitleEdit.Core; +using Nikse.Core; +using Nikse.SubtitleEdit.Core; using Nikse.SubtitleEdit.Logic; using System; using System.Globalization; @@ -72,12 +73,12 @@ namespace Nikse.SubtitleEdit.Controls { if (numericUpDown1.Value > NumericUpDownValue) { - SetTotalMilliseconds(milliseconds.Value + Logic.SubtitleFormats.SubtitleFormat.FramesToMilliseconds(1)); + SetTotalMilliseconds(milliseconds.Value + Core.SubtitleFormats.SubtitleFormat.FramesToMilliseconds(1)); } else if (numericUpDown1.Value < NumericUpDownValue) { if (milliseconds.Value - 100 > 0) - SetTotalMilliseconds(milliseconds.Value - Logic.SubtitleFormats.SubtitleFormat.FramesToMilliseconds(1)); + SetTotalMilliseconds(milliseconds.Value - Core.SubtitleFormats.SubtitleFormat.FramesToMilliseconds(1)); else if (milliseconds.Value > 0) SetTotalMilliseconds(0); } @@ -110,7 +111,7 @@ namespace Nikse.SubtitleEdit.Controls else { var tc = new TimeCode(milliseconds); - maskedTextBox1.Text = tc.ToString().Substring(0, 9) + string.Format("{0:00}", Logic.SubtitleFormats.SubtitleFormat.MillisecondsToFrames(tc.Milliseconds)); + maskedTextBox1.Text = tc.ToString().Substring(0, 9) + string.Format("{0:00}", Core.SubtitleFormats.SubtitleFormat.MillisecondsToFrames(tc.Milliseconds)); } } @@ -184,7 +185,7 @@ namespace Nikse.SubtitleEdit.Controls int milliSeconds; if (int.TryParse(times[3], out milliSeconds)) { - milliSeconds = Logic.SubtitleFormats.SubtitleFormat.FramesToMillisecondsMax999(milliSeconds); + milliSeconds = Core.SubtitleFormats.SubtitleFormat.FramesToMillisecondsMax999(milliSeconds); } return new TimeCode(hours, minutes, seconds, milliSeconds); diff --git a/src/Controls/VideoPlayerContainer.cs b/src/Controls/VideoPlayerContainer.cs index 57ee28216..765ef5c2c 100644 --- a/src/Controls/VideoPlayerContainer.cs +++ b/src/Controls/VideoPlayerContainer.cs @@ -1,4 +1,5 @@ -using Nikse.SubtitleEdit.Core; +using Nikse.Core; +using Nikse.SubtitleEdit.Core; using Nikse.SubtitleEdit.Logic; using Nikse.SubtitleEdit.Logic.VideoPlayers; using System; diff --git a/src/Forms/About.cs b/src/Forms/About.cs index 5728f1d7f..bf166b455 100644 --- a/src/Forms/About.cs +++ b/src/Forms/About.cs @@ -1,4 +1,4 @@ -using Nikse.SubtitleEdit.Logic; +using Nikse.SubtitleEdit.Core; using System; using System.Diagnostics; using System.Windows.Forms; diff --git a/src/Forms/AddToNames.cs b/src/Forms/AddToNames.cs index 14da1be53..c0819a5c0 100644 --- a/src/Forms/AddToNames.cs +++ b/src/Forms/AddToNames.cs @@ -1,8 +1,8 @@ -using Nikse.SubtitleEdit.Logic; +using Nikse.SubtitleEdit.Core; using System; using System.Collections.Generic; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic.Dictionaries; +using Nikse.SubtitleEdit.Core.Dictionaries; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/AddToOcrReplaceList.cs b/src/Forms/AddToOcrReplaceList.cs index 63b18b0b9..d1fbc8fab 100644 --- a/src/Forms/AddToOcrReplaceList.cs +++ b/src/Forms/AddToOcrReplaceList.cs @@ -1,8 +1,8 @@ -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.Dictionaries; +using Nikse.SubtitleEdit.Core; using System; using System.Globalization; using System.Windows.Forms; +using Nikse.SubtitleEdit.Core.Dictionaries; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/AddToUserDic.cs b/src/Forms/AddToUserDic.cs index 0836d5b60..ae62a7741 100644 --- a/src/Forms/AddToUserDic.cs +++ b/src/Forms/AddToUserDic.cs @@ -1,7 +1,7 @@ -using System; -using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; +using Nikse.SubtitleEdit.Core; +using System; using System.Collections.Generic; +using System.Windows.Forms; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/AddWaveForm.cs b/src/Forms/AddWaveForm.cs index 19f7c7415..701334f80 100644 --- a/src/Forms/AddWaveForm.cs +++ b/src/Forms/AddWaveForm.cs @@ -1,6 +1,6 @@ -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.ContainerFormats.Matroska; -using Nikse.SubtitleEdit.Logic.ContainerFormats.Mp4; +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.ContainerFormats.Matroska; +using Nikse.SubtitleEdit.Core.ContainerFormats.Mp4; using System; using System.Collections.Generic; using System.Diagnostics; diff --git a/src/Forms/AddWaveformBatch.cs b/src/Forms/AddWaveformBatch.cs index 4ce1cee1d..04459ed04 100644 --- a/src/Forms/AddWaveformBatch.cs +++ b/src/Forms/AddWaveformBatch.cs @@ -1,11 +1,10 @@ -using System; +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.ContainerFormats.Matroska; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Windows.Forms; -using Nikse.SubtitleEdit.Core; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.ContainerFormats.Matroska; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/AdjustDisplayDuration.cs b/src/Forms/AdjustDisplayDuration.cs index 603227ac5..64c0d8a53 100644 --- a/src/Forms/AdjustDisplayDuration.cs +++ b/src/Forms/AdjustDisplayDuration.cs @@ -1,7 +1,7 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.Globalization; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/AlignmentPicker.cs b/src/Forms/AlignmentPicker.cs index c980e1898..340970ecd 100644 --- a/src/Forms/AlignmentPicker.cs +++ b/src/Forms/AlignmentPicker.cs @@ -1,7 +1,7 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.Drawing; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/ApplyDurationLimits.cs b/src/Forms/ApplyDurationLimits.cs index 0d10a5303..6b19b4bcc 100644 --- a/src/Forms/ApplyDurationLimits.cs +++ b/src/Forms/ApplyDurationLimits.cs @@ -1,7 +1,6 @@ -using System; -using System.Globalization; +using Nikse.SubtitleEdit.Core; +using System; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/AutoBreakUnbreakLines.cs b/src/Forms/AutoBreakUnbreakLines.cs index 1cfcd40d4..b5d265a26 100644 --- a/src/Forms/AutoBreakUnbreakLines.cs +++ b/src/Forms/AutoBreakUnbreakLines.cs @@ -1,9 +1,8 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; +using System.Collections.Generic; using System.Globalization; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; -using System.Collections.Generic; -using Nikse.SubtitleEdit.Core; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/BatchConvert.cs b/src/Forms/BatchConvert.cs index 5b8f28666..0db649042 100644 --- a/src/Forms/BatchConvert.cs +++ b/src/Forms/BatchConvert.cs @@ -1,9 +1,10 @@ using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.BluRaySup; +using Nikse.SubtitleEdit.Core.ContainerFormats.Matroska; +using Nikse.SubtitleEdit.Core.Forms; +using Nikse.SubtitleEdit.Core.SubtitleFormats; using Nikse.SubtitleEdit.Forms.Styles; using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.BluRaySup; -using Nikse.SubtitleEdit.Logic.ContainerFormats.Matroska; -using Nikse.SubtitleEdit.Logic.SubtitleFormats; using System; using System.Collections.Generic; using System.ComponentModel; @@ -54,7 +55,7 @@ namespace Nikse.SubtitleEdit.Forms private string _assStyle; private string _ssaStyle; - private readonly Logic.Forms.RemoveTextForHI _removeTextForHearingImpaired; + private readonly RemoveTextForHI _removeTextForHearingImpaired; private readonly ChangeCasing _changeCasing = new ChangeCasing(); private readonly ChangeCasingNames _changeCasingNames = new ChangeCasingNames(); private bool _converting; @@ -193,7 +194,7 @@ namespace Nikse.SubtitleEdit.Forms radioButtonShowLater.Text = Configuration.Settings.Language.ShowEarlierLater.ShowLater; checkBoxSetMinimumDisplayTimeBetweenSubs.Text = l.SetMinMsBetweenSubtitles; - _removeTextForHearingImpaired = new Logic.Forms.RemoveTextForHI(new Logic.Forms.RemoveTextForHISettings()); + _removeTextForHearingImpaired = new RemoveTextForHI(new RemoveTextForHISettings()); labelFilter.Text = l.Filter; comboBoxFilter.Items[0] = Configuration.Settings.Language.General.AllFiles; @@ -923,7 +924,7 @@ namespace Nikse.SubtitleEdit.Forms { try { - p.Subtitle = Logic.Forms.SplitLongLinesHelper.SplitLongLinesInSubtitle(p.Subtitle, Configuration.Settings.General.SubtitleLineMaximumLength * 2, Configuration.Settings.General.SubtitleLineMaximumLength); + p.Subtitle = SplitLongLinesHelper.SplitLongLinesInSubtitle(p.Subtitle, Configuration.Settings.General.SubtitleLineMaximumLength * 2, Configuration.Settings.General.SubtitleLineMaximumLength); } catch (Exception exception) { diff --git a/src/Forms/Beamer.cs b/src/Forms/Beamer.cs index 05f306b41..a4b3d5802 100644 --- a/src/Forms/Beamer.cs +++ b/src/Forms/Beamer.cs @@ -1,5 +1,4 @@ using Nikse.SubtitleEdit.Core; -using Nikse.SubtitleEdit.Logic; using System; using System.Collections.Generic; using System.Drawing; diff --git a/src/Forms/ChangeCasing.cs b/src/Forms/ChangeCasing.cs index 02aaabfc1..20db3d61b 100644 --- a/src/Forms/ChangeCasing.cs +++ b/src/Forms/ChangeCasing.cs @@ -1,9 +1,8 @@ using Nikse.SubtitleEdit.Core; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.Dictionaries; using System; using System.Collections.Generic; using System.Windows.Forms; +using Nikse.SubtitleEdit.Core.Dictionaries; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/ChangeCasingNames.cs b/src/Forms/ChangeCasingNames.cs index c971f27d9..5afe389fc 100644 --- a/src/Forms/ChangeCasingNames.cs +++ b/src/Forms/ChangeCasingNames.cs @@ -1,11 +1,10 @@ using Nikse.SubtitleEdit.Core; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.Dictionaries; using System; using System.Collections.Generic; using System.Globalization; using System.Text; using System.Windows.Forms; +using Nikse.SubtitleEdit.Core.Dictionaries; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/ChangeFrameRate.cs b/src/Forms/ChangeFrameRate.cs index ee726f4cd..28b4845fd 100644 --- a/src/Forms/ChangeFrameRate.cs +++ b/src/Forms/ChangeFrameRate.cs @@ -1,6 +1,7 @@ -using System; -using System.Windows.Forms; +using Nikse.SubtitleEdit.Core; using Nikse.SubtitleEdit.Logic; +using System; +using System.Windows.Forms; namespace Nikse.SubtitleEdit.Forms { @@ -49,7 +50,7 @@ namespace Nikse.SubtitleEdit.Forms openFileDialog1.FileName = string.Empty; if (openFileDialog1.ShowDialog() == DialogResult.OK) { - VideoInfo info = Utilities.GetVideoInfo(openFileDialog1.FileName); + VideoInfo info = UiUtil.GetVideoInfo(openFileDialog1.FileName); if (info != null && info.Success) { return info.FramesPerSecond.ToString(); diff --git a/src/Forms/ChangeSpeedInPercent.cs b/src/Forms/ChangeSpeedInPercent.cs index 99b664d87..9892fd08b 100644 --- a/src/Forms/ChangeSpeedInPercent.cs +++ b/src/Forms/ChangeSpeedInPercent.cs @@ -1,6 +1,6 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/CheckForUpdates.cs b/src/Forms/CheckForUpdates.cs index e348ff893..2cdb93481 100644 --- a/src/Forms/CheckForUpdates.cs +++ b/src/Forms/CheckForUpdates.cs @@ -1,7 +1,7 @@ -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.Forms; +using Nikse.SubtitleEdit.Core; using System; using System.Windows.Forms; +using Nikse.SubtitleEdit.Core.Forms; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/ChooseAudioTrack.cs b/src/Forms/ChooseAudioTrack.cs index bcff0bec6..deb9eb92e 100644 --- a/src/Forms/ChooseAudioTrack.cs +++ b/src/Forms/ChooseAudioTrack.cs @@ -1,7 +1,7 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.Collections.Generic; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/ChooseEncoding.cs b/src/Forms/ChooseEncoding.cs index 5b209f56a..742bd96a4 100644 --- a/src/Forms/ChooseEncoding.cs +++ b/src/Forms/ChooseEncoding.cs @@ -1,8 +1,8 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.IO; using System.Text; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/ChooseLanguage.cs b/src/Forms/ChooseLanguage.cs index c38fb1774..3ab860b03 100644 --- a/src/Forms/ChooseLanguage.cs +++ b/src/Forms/ChooseLanguage.cs @@ -1,10 +1,10 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; using System.Xml; namespace Nikse.SubtitleEdit.Forms diff --git a/src/Forms/ChooseResolution.cs b/src/Forms/ChooseResolution.cs index fbf3dd027..9e98cfee7 100644 --- a/src/Forms/ChooseResolution.cs +++ b/src/Forms/ChooseResolution.cs @@ -1,6 +1,7 @@ -using System; -using System.Windows.Forms; +using Nikse.SubtitleEdit.Core; using Nikse.SubtitleEdit.Logic; +using System; +using System.Windows.Forms; namespace Nikse.SubtitleEdit.Forms { @@ -26,7 +27,7 @@ namespace Nikse.SubtitleEdit.Forms openFileDialog1.FileName = string.Empty; if (openFileDialog1.ShowDialog() == DialogResult.OK) { - VideoInfo info = Utilities.GetVideoInfo(openFileDialog1.FileName); + VideoInfo info = UiUtil.GetVideoInfo(openFileDialog1.FileName); if (info != null && info.Success) { numericUpDownVideoWidth.Value = info.Width; diff --git a/src/Forms/ChooseStyle.cs b/src/Forms/ChooseStyle.cs index dc98266b5..44dd346e0 100644 --- a/src/Forms/ChooseStyle.cs +++ b/src/Forms/ChooseStyle.cs @@ -1,6 +1,6 @@ -using Nikse.SubtitleEdit.Forms.Styles; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.SubtitleFormats; +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.SubtitleFormats; +using Nikse.SubtitleEdit.Forms.Styles; using System; using System.Windows.Forms; diff --git a/src/Forms/ColorChooser.cs b/src/Forms/ColorChooser.cs index 312c4f880..dd5b3f5d2 100644 --- a/src/Forms/ColorChooser.cs +++ b/src/Forms/ColorChooser.cs @@ -17,7 +17,7 @@ #region Using directives -using Nikse.SubtitleEdit.Logic; +using Nikse.SubtitleEdit.Core; using Nikse.SubtitleEdit.Logic.ColorChooser; using System; using System.Drawing; diff --git a/src/Forms/ColumnPaste.cs b/src/Forms/ColumnPaste.cs index db116f5e6..0f2c95e57 100644 --- a/src/Forms/ColumnPaste.cs +++ b/src/Forms/ColumnPaste.cs @@ -1,6 +1,6 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/Compare.cs b/src/Forms/Compare.cs index db4557c98..37966979b 100644 --- a/src/Forms/Compare.cs +++ b/src/Forms/Compare.cs @@ -1,7 +1,6 @@ using Nikse.SubtitleEdit.Controls; using Nikse.SubtitleEdit.Core; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.SubtitleFormats; +using Nikse.SubtitleEdit.Core.SubtitleFormats; using System; using System.Collections.Generic; using System.Drawing; diff --git a/src/Forms/DCinema/DCinemaPropertiesInterop.cs b/src/Forms/DCinema/DCinemaPropertiesInterop.cs index 301e9bfeb..4aa2f5ea7 100644 --- a/src/Forms/DCinema/DCinemaPropertiesInterop.cs +++ b/src/Forms/DCinema/DCinemaPropertiesInterop.cs @@ -1,6 +1,7 @@ using System; using System.Globalization; using System.Windows.Forms; +using Nikse.SubtitleEdit.Core; using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms.DCinema diff --git a/src/Forms/DCinema/DCinemaPropertiesSmpte.cs b/src/Forms/DCinema/DCinemaPropertiesSmpte.cs index 73677f4c1..b3a3f399e 100644 --- a/src/Forms/DCinema/DCinemaPropertiesSmpte.cs +++ b/src/Forms/DCinema/DCinemaPropertiesSmpte.cs @@ -1,6 +1,7 @@ using System; using System.Globalization; using System.Windows.Forms; +using Nikse.SubtitleEdit.Core; using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms.DCinema diff --git a/src/Forms/DialogDoNotShowAgain.cs b/src/Forms/DialogDoNotShowAgain.cs index c4bd5aa2b..3b226ebf5 100644 --- a/src/Forms/DialogDoNotShowAgain.cs +++ b/src/Forms/DialogDoNotShowAgain.cs @@ -1,4 +1,4 @@ -using Nikse.SubtitleEdit.Logic; +using Nikse.SubtitleEdit.Core; using System; using System.Drawing; using System.Windows.Forms; diff --git a/src/Forms/DoNotBreakAfterListEdit.cs b/src/Forms/DoNotBreakAfterListEdit.cs index bea83dc5b..d2913e9e5 100644 --- a/src/Forms/DoNotBreakAfterListEdit.cs +++ b/src/Forms/DoNotBreakAfterListEdit.cs @@ -1,11 +1,11 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Text.RegularExpressions; using System.Windows.Forms; using System.Xml; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/DurationsBridgeGaps.cs b/src/Forms/DurationsBridgeGaps.cs index d2ae93b21..c3e3f6697 100644 --- a/src/Forms/DurationsBridgeGaps.cs +++ b/src/Forms/DurationsBridgeGaps.cs @@ -1,8 +1,8 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/DvdSubRip.cs b/src/Forms/DvdSubRip.cs index e6198a994..2c0e06602 100644 --- a/src/Forms/DvdSubRip.cs +++ b/src/Forms/DvdSubRip.cs @@ -1,13 +1,12 @@ using System.Globalization; -using Nikse.SubtitleEdit.Core; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.VobSub; using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Text; using System.Windows.Forms; +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.VobSub; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/DvdSubRipChooseLanguage.cs b/src/Forms/DvdSubRipChooseLanguage.cs index 748f2a527..234514b67 100644 --- a/src/Forms/DvdSubRipChooseLanguage.cs +++ b/src/Forms/DvdSubRipChooseLanguage.cs @@ -1,9 +1,9 @@ -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.VobSub; -using System; +using System; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.VobSub; namespace Nikse.SubtitleEdit.Forms { @@ -179,7 +179,7 @@ namespace Nikse.SubtitleEdit.Forms using (var exportBdnXmlPng = new ExportPngXml()) { - exportBdnXmlPng.InitializeFromVobSubOcr(subtitle, new Logic.SubtitleFormats.SubRip(), "VOBSUB", "DVD", formSubOcr, SelectedLanguageString); + exportBdnXmlPng.InitializeFromVobSubOcr(subtitle, new Core.SubtitleFormats.SubRip(), "VOBSUB", "DVD", formSubOcr, SelectedLanguageString); exportBdnXmlPng.ShowDialog(this); } } diff --git a/src/Forms/EbuSaveOptions.cs b/src/Forms/EbuSaveOptions.cs index 81a713b7d..8a066b608 100644 --- a/src/Forms/EbuSaveOptions.cs +++ b/src/Forms/EbuSaveOptions.cs @@ -1,6 +1,5 @@ using Nikse.SubtitleEdit.Core; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.SubtitleFormats; +using Nikse.SubtitleEdit.Core.SubtitleFormats; using System; using System.Globalization; using System.IO; diff --git a/src/Forms/EffectKaraoke.cs b/src/Forms/EffectKaraoke.cs index 60f2f676e..b8ac375ba 100644 --- a/src/Forms/EffectKaraoke.cs +++ b/src/Forms/EffectKaraoke.cs @@ -1,10 +1,9 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.Collections.Generic; using System.Drawing; using System.Text; using System.Windows.Forms; -using Nikse.SubtitleEdit.Core; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/EffectTypewriter.cs b/src/Forms/EffectTypewriter.cs index 9bed9928d..80110b18e 100644 --- a/src/Forms/EffectTypewriter.cs +++ b/src/Forms/EffectTypewriter.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Drawing; using System.Text; using System.Windows.Forms; +using Nikse.Core; using Nikse.SubtitleEdit.Logic; using Nikse.SubtitleEdit.Core; diff --git a/src/Forms/ExportCustomText.cs b/src/Forms/ExportCustomText.cs index 9fe54173a..3ec27854e 100644 --- a/src/Forms/ExportCustomText.cs +++ b/src/Forms/ExportCustomText.cs @@ -1,4 +1,4 @@ -using Nikse.SubtitleEdit.Logic; +using Nikse.SubtitleEdit.Core; using System; using System.Collections.Generic; using System.IO; diff --git a/src/Forms/ExportCustomTextFormat.cs b/src/Forms/ExportCustomTextFormat.cs index b3b9655bf..1567e6536 100644 --- a/src/Forms/ExportCustomTextFormat.cs +++ b/src/Forms/ExportCustomTextFormat.cs @@ -1,8 +1,8 @@ -using System; +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.SubtitleFormats; +using System; using System.Globalization; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.SubtitleFormats; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/ExportPngXml.cs b/src/Forms/ExportPngXml.cs index fe91127be..605083e5e 100644 --- a/src/Forms/ExportPngXml.cs +++ b/src/Forms/ExportPngXml.cs @@ -1,8 +1,9 @@ -using Nikse.SubtitleEdit.Core; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.SubtitleFormats; +using Nikse.Core; +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.BluRaySup; +using Nikse.SubtitleEdit.Core.SubtitleFormats; +using Nikse.SubtitleEdit.Core.VobSub; using Nikse.SubtitleEdit.Logic.VideoPlayers; -using Nikse.SubtitleEdit.Logic.VobSub; using System; using System.Collections.Generic; using System.Drawing; @@ -248,7 +249,7 @@ namespace Nikse.SubtitleEdit.Forms private static void MakeBluRaySupImage(MakeBitmapParameter param) { - var brSub = new Logic.BluRaySup.BluRaySupPicture + var brSub = new BluRaySupPicture { StartTime = (long)param.P.StartTime.TotalMilliseconds, EndTime = (long)param.P.EndTime.TotalMilliseconds, @@ -284,14 +285,14 @@ namespace Nikse.SubtitleEdit.Forms g.DrawImage(bmp, left, top); g.Dispose(); } - param.Buffer = Logic.BluRaySup.BluRaySupPicture.CreateSupFrame(brSub, fullSize, param.FramesPerSeconds, 0, 0, ContentAlignment.BottomCenter); + param.Buffer = BluRaySupPicture.CreateSupFrame(brSub, fullSize, param.FramesPerSeconds, 0, 0, ContentAlignment.BottomCenter); } } } } else { - param.Buffer = Logic.BluRaySup.BluRaySupPicture.CreateSupFrame(brSub, param.Bitmap, param.FramesPerSeconds, param.BottomMargin, param.LeftRightMargin, param.Alignment); + param.Buffer = BluRaySupPicture.CreateSupFrame(brSub, param.Bitmap, param.FramesPerSeconds, param.BottomMargin, param.LeftRightMargin, param.Alignment); } } @@ -3844,7 +3845,7 @@ $DROP=[DROPVALUE]" + Environment.NewLine + Environment.NewLine + subtitleListView1.Columns[columnIndexText].Width = -2; } - private void SubtitleListView1InitializeLanguage(LanguageStructure.General general, Logic.Settings settings) + private void SubtitleListView1InitializeLanguage(LanguageStructure.General general, Core.Settings settings) { int columnIndexNumber = 0; int columnIndexStart = 1; diff --git a/src/Forms/ExportPngXmlPreview.cs b/src/Forms/ExportPngXmlPreview.cs index 1413f7815..8bc69b662 100644 --- a/src/Forms/ExportPngXmlPreview.cs +++ b/src/Forms/ExportPngXmlPreview.cs @@ -1,6 +1,6 @@ -using System.Drawing; +using Nikse.SubtitleEdit.Core; +using System.Drawing; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/ExportText.cs b/src/Forms/ExportText.cs index 651627594..9ec93a752 100644 --- a/src/Forms/ExportText.cs +++ b/src/Forms/ExportText.cs @@ -1,9 +1,8 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.IO; using System.Text; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Core; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/ExportTextST.cs b/src/Forms/ExportTextST.cs index 40714fae9..38af3ffe3 100644 --- a/src/Forms/ExportTextST.cs +++ b/src/Forms/ExportTextST.cs @@ -1,15 +1,11 @@ -// WORK IN PROGRESS - DO NOT REFACTOR // -// WORK IN PROGRESS - DO NOT REFACTOR // -// WORK IN PROGRESS - DO NOT REFACTOR // - +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.SubtitleFormats; using System; using System.Collections.Generic; using System.Drawing; using System.IO; -using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.SubtitleFormats; using System.Linq; +using System.Windows.Forms; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/ExtractDateTimeInfo.cs b/src/Forms/ExtractDateTimeInfo.cs index 6eb11a495..6e5816086 100644 --- a/src/Forms/ExtractDateTimeInfo.cs +++ b/src/Forms/ExtractDateTimeInfo.cs @@ -1,10 +1,11 @@ -using System; +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.ContainerFormats.Mp4; +using Nikse.SubtitleEdit.Logic; +using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.ContainerFormats.Mp4; namespace Nikse.SubtitleEdit.Forms { @@ -78,7 +79,7 @@ namespace Nikse.SubtitleEdit.Forms { var fi = new FileInfo(VideoFileName); start = fi.CreationTime; - VideoInfo vi = Utilities.GetVideoInfo(VideoFileName); + VideoInfo vi = UiUtil.GetVideoInfo(VideoFileName); durationInSeconds = vi.TotalMilliseconds / TimeCode.BaseUnit; if (durationInSeconds < 1) { diff --git a/src/Forms/FcpProperties.cs b/src/Forms/FcpProperties.cs index 3b7827149..d95d1e467 100644 --- a/src/Forms/FcpProperties.cs +++ b/src/Forms/FcpProperties.cs @@ -1,6 +1,6 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/FindDialog.cs b/src/Forms/FindDialog.cs index 8abb774bf..82a2c5cca 100644 --- a/src/Forms/FindDialog.cs +++ b/src/Forms/FindDialog.cs @@ -1,9 +1,9 @@ -using System; +using Nikse.Core.Enums; +using Nikse.SubtitleEdit.Core; +using System; using System.Drawing; using System.Text.RegularExpressions; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.Enums; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/FindSubtitleLine.cs b/src/Forms/FindSubtitleLine.cs index 5c3a63261..d26e037e1 100644 --- a/src/Forms/FindSubtitleLine.cs +++ b/src/Forms/FindSubtitleLine.cs @@ -1,8 +1,7 @@ -using System; -using System.Windows.Forms; -using Nikse.SubtitleEdit.Core; -using Nikse.SubtitleEdit.Logic; +using Nikse.SubtitleEdit.Core; +using System; using System.Collections.Generic; +using System.Windows.Forms; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/FixCommonErrors.cs b/src/Forms/FixCommonErrors.cs index da75cc110..14e0e3c05 100644 --- a/src/Forms/FixCommonErrors.cs +++ b/src/Forms/FixCommonErrors.cs @@ -1,9 +1,8 @@ using Nikse.SubtitleEdit.Core; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.Dictionaries; -using Nikse.SubtitleEdit.Logic.Forms; +using Nikse.SubtitleEdit.Core.Dictionaries; +using Nikse.SubtitleEdit.Core.Forms; +using Nikse.SubtitleEdit.Core.SubtitleFormats; using Nikse.SubtitleEdit.Logic.Ocr; -using Nikse.SubtitleEdit.Logic.SubtitleFormats; using System; using System.Collections.Generic; using System.Drawing; diff --git a/src/Forms/GetDictionaries.cs b/src/Forms/GetDictionaries.cs index f7b8e902a..d13118393 100644 --- a/src/Forms/GetDictionaries.cs +++ b/src/Forms/GetDictionaries.cs @@ -1,4 +1,4 @@ -using Nikse.SubtitleEdit.Logic; +using Nikse.SubtitleEdit.Core; using System; using System.Collections.Generic; using System.IO; diff --git a/src/Forms/GetTesseractDictionaries.cs b/src/Forms/GetTesseractDictionaries.cs index 73e89db56..a95143893 100644 --- a/src/Forms/GetTesseractDictionaries.cs +++ b/src/Forms/GetTesseractDictionaries.cs @@ -1,11 +1,11 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Net; using System.Windows.Forms; using System.Xml; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/GoToLine.cs b/src/Forms/GoToLine.cs index 179e37b37..510bca2d0 100644 --- a/src/Forms/GoToLine.cs +++ b/src/Forms/GoToLine.cs @@ -1,6 +1,6 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/GoogleOrMicrosoftTranslate.cs b/src/Forms/GoogleOrMicrosoftTranslate.cs index 58b031ae3..493d7d759 100644 --- a/src/Forms/GoogleOrMicrosoftTranslate.cs +++ b/src/Forms/GoogleOrMicrosoftTranslate.cs @@ -1,6 +1,6 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/GoogleTranslate.cs b/src/Forms/GoogleTranslate.cs index 28163bbfa..67c0e1190 100644 --- a/src/Forms/GoogleTranslate.cs +++ b/src/Forms/GoogleTranslate.cs @@ -1,5 +1,4 @@ using Nikse.SubtitleEdit.Core; -using Nikse.SubtitleEdit.Logic; using System; using System.IO; using System.Net; diff --git a/src/Forms/HardSubExtract.cs b/src/Forms/HardSubExtract.cs index d2318a5fc..c1bd7b750 100644 --- a/src/Forms/HardSubExtract.cs +++ b/src/Forms/HardSubExtract.cs @@ -1,9 +1,10 @@ -using System; +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Logic; +using Nikse.SubtitleEdit.Logic.VideoPlayers; +using System; using System.Drawing; using System.IO; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.VideoPlayers; //using Nikse.SubtitleEdit.Logic.DirectShow.Custom; namespace Nikse.SubtitleEdit.Forms @@ -39,10 +40,10 @@ namespace Nikse.SubtitleEdit.Forms { _videoFileName = openFileDialogVideo.FileName; tbFileName.Text = openFileDialogVideo.FileName; - _videoInfo = Utilities.GetVideoInfo(_videoFileName); + _videoInfo = UiUtil.GetVideoInfo(_videoFileName); var oldPlayer = Configuration.Settings.General.VideoPlayer; Configuration.Settings.General.VideoPlayer = "VLC"; - Utilities.InitializeVideoPlayerAndContainer(_videoFileName, _videoInfo, mediaPlayer, VideoLoaded, null); + UiUtil.InitializeVideoPlayerAndContainer(_videoFileName, _videoInfo, mediaPlayer, VideoLoaded, null); Configuration.Settings.General.VideoPlayer = oldPlayer; _libVlc = mediaPlayer.VideoPlayer as LibVlcDynamic; } diff --git a/src/Forms/ImportImages.cs b/src/Forms/ImportImages.cs index 1fa196fd2..943fbd0ba 100644 --- a/src/Forms/ImportImages.cs +++ b/src/Forms/ImportImages.cs @@ -1,4 +1,4 @@ -using Nikse.SubtitleEdit.Logic; +using Nikse.SubtitleEdit.Core; using System; using System.IO; using System.Windows.Forms; diff --git a/src/Forms/ImportSceneChanges.cs b/src/Forms/ImportSceneChanges.cs index ea37a0eac..67f735e2d 100644 --- a/src/Forms/ImportSceneChanges.cs +++ b/src/Forms/ImportSceneChanges.cs @@ -1,10 +1,9 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Windows.Forms; -using Nikse.SubtitleEdit.Core; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/ImportText.cs b/src/Forms/ImportText.cs index 57ada7930..588ef7082 100644 --- a/src/Forms/ImportText.cs +++ b/src/Forms/ImportText.cs @@ -1,12 +1,11 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Text; using System.Windows.Forms; using System.Xml; -using Nikse.SubtitleEdit.Core; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/ImportUnknownFormat.cs b/src/Forms/ImportUnknownFormat.cs index 6c24195b0..d481a85c8 100644 --- a/src/Forms/ImportUnknownFormat.cs +++ b/src/Forms/ImportUnknownFormat.cs @@ -1,8 +1,8 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.IO; using System.Text; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/Interjections.cs b/src/Forms/Interjections.cs index 2cbdea5b2..1dcf87e06 100644 --- a/src/Forms/Interjections.cs +++ b/src/Forms/Interjections.cs @@ -1,8 +1,8 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.Collections.Generic; using System.Text; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/JoinSubtitles.cs b/src/Forms/JoinSubtitles.cs index c82798526..4d3ae2a78 100644 --- a/src/Forms/JoinSubtitles.cs +++ b/src/Forms/JoinSubtitles.cs @@ -1,9 +1,9 @@ -using System; +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.SubtitleFormats; +using System; using System.Collections.Generic; using System.Text; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.SubtitleFormats; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/Main.cs b/src/Forms/Main.cs index b7ace4f20..c78c89e38 100644 --- a/src/Forms/Main.cs +++ b/src/Forms/Main.cs @@ -1,18 +1,20 @@ -using Nikse.SubtitleEdit.Controls; +using Nikse.Core; +using Nikse.Core.Enums; +using Nikse.SubtitleEdit.Controls; using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.BluRaySup; +using Nikse.SubtitleEdit.Core.ContainerFormats.MaterialExchangeFormat; +using Nikse.SubtitleEdit.Core.ContainerFormats.Matroska; +using Nikse.SubtitleEdit.Core.ContainerFormats.Mp4; +using Nikse.SubtitleEdit.Core.ContainerFormats.Mp4.Boxes; +using Nikse.SubtitleEdit.Core.Forms; +using Nikse.SubtitleEdit.Core.SubtitleFormats; +using Nikse.SubtitleEdit.Core.TransportStream; +using Nikse.SubtitleEdit.Core.VobSub; using Nikse.SubtitleEdit.Forms.Styles; using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.BluRaySup; -using Nikse.SubtitleEdit.Logic.ContainerFormats.MaterialExchangeFormat; -using Nikse.SubtitleEdit.Logic.ContainerFormats.Matroska; -using Nikse.SubtitleEdit.Logic.ContainerFormats.Mp4; -using Nikse.SubtitleEdit.Logic.ContainerFormats.Mp4.Boxes; -using Nikse.SubtitleEdit.Logic.Enums; using Nikse.SubtitleEdit.Logic.Networking; -using Nikse.SubtitleEdit.Logic.SubtitleFormats; -using Nikse.SubtitleEdit.Logic.TransportStream; using Nikse.SubtitleEdit.Logic.VideoPlayers; -using Nikse.SubtitleEdit.Logic.VobSub; using System; using System.Collections.Generic; using System.Drawing; @@ -97,7 +99,7 @@ namespace Nikse.SubtitleEdit.Forms private StringBuilder _statusLog = new StringBuilder(); private bool _makeHistoryPaused = false; - private Logic.Forms.CheckForUpdatesHelper _checkForUpdatesHelper; + private CheckForUpdatesHelper _checkForUpdatesHelper; private Timer _timerCheckForUpdates; private NikseWebServiceSession _networkSession; @@ -333,6 +335,10 @@ namespace Nikse.SubtitleEdit.Forms } SetEncoding(Configuration.Settings.General.DefaultEncoding); + // set up UI interfaces in subtitle formats + (SubtitleFormat.AllSubtitleFormats.First(p => p.Name == YouTubeAnnotations.NameOfFormat) as YouTubeAnnotations).GetYouTubeAnnotationStyles = new UiGetYouTubeAnnotationStyles(); + Ebu.EbuUiHelper = new UiEbuSaveHelper(); + toolStripComboBoxFrameRate.Items.Add((23.976).ToString()); toolStripComboBoxFrameRate.Items.Add((24.0).ToString()); toolStripComboBoxFrameRate.Items.Add((25.0).ToString()); @@ -895,7 +901,7 @@ namespace Nikse.SubtitleEdit.Forms { Paragraph originalNext = null; if (_subtitleAlternate != null && SubtitleListview1.IsAlternateTextColumnVisible) - originalNext = Nikse.SubtitleEdit.Logic.Utilities.GetOriginalParagraph(index + 1, next, _subtitleAlternate.Paragraphs); + originalNext = Utilities.GetOriginalParagraph(index + 1, next, _subtitleAlternate.Paragraphs); next.StartTime.TotalMilliseconds = next.StartTime.TotalMilliseconds + (e.Paragraph.EndTime.TotalMilliseconds - beforeParagraph.EndTime.TotalMilliseconds); SubtitleListview1.SetStartTimeAndDuration(index + 1, next); @@ -3633,10 +3639,10 @@ namespace Nikse.SubtitleEdit.Forms private int ShowSubtitle() { if (_splitDualSami) - return Utilities.ShowSubtitle(_subtitle.Paragraphs, _subtitleAlternate, mediaPlayer); + return UiUtil.ShowSubtitle(_subtitle.Paragraphs, _subtitleAlternate, mediaPlayer); if (SubtitleListview1.IsAlternateTextColumnVisible && Configuration.Settings.General.ShowOriginalAsPreviewIfAvailable) - return Utilities.ShowSubtitle(_subtitleAlternate.Paragraphs, mediaPlayer); - return Utilities.ShowSubtitle(_subtitle.Paragraphs, mediaPlayer); + return UiUtil.ShowSubtitle(_subtitleAlternate.Paragraphs, mediaPlayer); + return UiUtil.ShowSubtitle(_subtitle.Paragraphs, mediaPlayer); } private static void TryLoadIcon(ToolStripButton button, string iconName) @@ -3792,7 +3798,7 @@ namespace Nikse.SubtitleEdit.Forms if (openFileDialog1.ShowDialog(this) == DialogResult.OK) { _videoFileName = openFileDialog1.FileName; - var info = Utilities.GetVideoInfo(openFileDialog1.FileName); + var info = UiUtil.GetVideoInfo(openFileDialog1.FileName); if (info != null && info.Success) { string oldFrameRate = toolStripComboBoxFrameRate.Text; @@ -8933,15 +8939,15 @@ namespace Nikse.SubtitleEdit.Forms _subtitle.Paragraphs.Clear(); List mergedVobSubPacks = new List(); - Nikse.SubtitleEdit.Logic.VobSub.Idx idx = new Logic.VobSub.Idx(matroskaSubtitleInfo.CodecPrivate.SplitToLines()); + var idx = new Nikse.SubtitleEdit.Core.VobSub.Idx(matroskaSubtitleInfo.CodecPrivate.SplitToLines()); foreach (var p in sub) { if (matroskaSubtitleInfo.ContentEncodingType == 0) // compressed with zlib { bool error = false; - MemoryStream outStream = new MemoryStream(); + var outStream = new MemoryStream(); var outZStream = new zlib.ZOutputStream(outStream); - MemoryStream inStream = new MemoryStream(p.Data); + var inStream = new MemoryStream(p.Data); byte[] buffer = null; try { @@ -12676,7 +12682,7 @@ namespace Nikse.SubtitleEdit.Forms if (_videoInfo.FramesPerSecond > 0) toolStripComboBoxFrameRate.Text = string.Format("{0:0.###}", _videoInfo.FramesPerSecond); - Utilities.InitializeVideoPlayerAndContainer(fileName, _videoInfo, mediaPlayer, VideoLoaded, VideoEnded); + UiUtil.InitializeVideoPlayerAndContainer(fileName, _videoInfo, mediaPlayer, VideoLoaded, VideoEnded); mediaPlayer.ShowFullscreenButton = Configuration.Settings.General.VideoPlayerShowFullscreenButton; mediaPlayer.OnButtonClicked -= MediaPlayer_OnButtonClicked; mediaPlayer.OnButtonClicked += MediaPlayer_OnButtonClicked; @@ -12764,7 +12770,7 @@ namespace Nikse.SubtitleEdit.Forms private static VideoInfo ShowVideoInfo(string fileName) { - return Utilities.GetVideoInfo(fileName); + return UiUtil.GetVideoInfo(fileName); } private void TryToFindAndOpenVideoFile(string fileNameNoExtension) @@ -14165,7 +14171,7 @@ namespace Nikse.SubtitleEdit.Forms } else if (Configuration.Settings.General.CheckForUpdates && Configuration.Settings.General.LastCheckForUpdates < DateTime.Now.AddDays(-5)) { - _checkForUpdatesHelper = new Nikse.SubtitleEdit.Logic.Forms.CheckForUpdatesHelper(); + _checkForUpdatesHelper = new CheckForUpdatesHelper(); _checkForUpdatesHelper.CheckForUpdates(); _timerCheckForUpdates = new Timer(); _timerCheckForUpdates.Interval = 7000; @@ -15046,7 +15052,7 @@ namespace Nikse.SubtitleEdit.Forms if (p != null) { mediaPlayer.CurrentPosition = p.StartTime.TotalSeconds; - Utilities.ShowSubtitle(_subtitle.Paragraphs, mediaPlayer); + UiUtil.ShowSubtitle(_subtitle.Paragraphs, mediaPlayer); mediaPlayer.Play(); _endSeconds = p.EndTime.TotalSeconds; } @@ -17434,7 +17440,7 @@ namespace Nikse.SubtitleEdit.Forms fileName = fileName.Substring(0, fileName.Length - 1); fileName += pac.Extension; } - pac.Save(fileName, _subtitle); + pac.Save(fileName, _subtitle, new UiGetPacEncoding()); } } @@ -19172,7 +19178,7 @@ namespace Nikse.SubtitleEdit.Forms try { Configuration.Settings.General.VideoPlayer = "VLC"; - Utilities.InitializeVideoPlayerAndContainer(VideoFileName, _videoInfo, mediaPlayer, VideoLoaded, VideoEnded); + UiUtil.InitializeVideoPlayerAndContainer(VideoFileName, _videoInfo, mediaPlayer, VideoLoaded, VideoEnded); mediaPlayer.ShowFullscreenButton = Configuration.Settings.General.VideoPlayerShowFullscreenButton; mediaPlayer.OnButtonClicked -= MediaPlayer_OnButtonClicked; mediaPlayer.OnButtonClicked += MediaPlayer_OnButtonClicked; diff --git a/src/Forms/MatroskaSubtitleChooser.cs b/src/Forms/MatroskaSubtitleChooser.cs index 812419246..d2f27df14 100644 --- a/src/Forms/MatroskaSubtitleChooser.cs +++ b/src/Forms/MatroskaSubtitleChooser.cs @@ -1,6 +1,6 @@ -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.ContainerFormats.Matroska; -using Nikse.SubtitleEdit.Logic.ContainerFormats.Mp4.Boxes; +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.ContainerFormats.Matroska; +using Nikse.SubtitleEdit.Core.ContainerFormats.Mp4.Boxes; using System; using System.Collections.Generic; using System.Windows.Forms; diff --git a/src/Forms/MeasurementConverter.cs b/src/Forms/MeasurementConverter.cs index 89e8b8112..3efdefdad 100644 --- a/src/Forms/MeasurementConverter.cs +++ b/src/Forms/MeasurementConverter.cs @@ -1,7 +1,7 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.Drawing; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/MergeDoubleLines.cs b/src/Forms/MergeDoubleLines.cs index 20a75fe3f..9a6b7eab2 100644 --- a/src/Forms/MergeDoubleLines.cs +++ b/src/Forms/MergeDoubleLines.cs @@ -1,9 +1,8 @@ -using System.Globalization; -using Nikse.SubtitleEdit.Core; -using Nikse.SubtitleEdit.Logic; +using Nikse.SubtitleEdit.Core; using System; using System.Collections.Generic; using System.Drawing; +using System.Globalization; using System.Text; using System.Windows.Forms; diff --git a/src/Forms/MergeShortLines.cs b/src/Forms/MergeShortLines.cs index 913858887..551f08290 100644 --- a/src/Forms/MergeShortLines.cs +++ b/src/Forms/MergeShortLines.cs @@ -1,11 +1,10 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.Collections.Generic; using System.Drawing; using System.Globalization; using System.Text; using System.Windows.Forms; -using Nikse.SubtitleEdit.Core; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/MergeTextWithSameTimeCodes.cs b/src/Forms/MergeTextWithSameTimeCodes.cs index 40601c1ec..4c281d6ca 100644 --- a/src/Forms/MergeTextWithSameTimeCodes.cs +++ b/src/Forms/MergeTextWithSameTimeCodes.cs @@ -1,4 +1,4 @@ -using Nikse.SubtitleEdit.Logic; +using Nikse.SubtitleEdit.Core; using System; using System.Collections.Generic; using System.Drawing; diff --git a/src/Forms/ModifySelection.cs b/src/Forms/ModifySelection.cs index 388705d08..48c04d323 100644 --- a/src/Forms/ModifySelection.cs +++ b/src/Forms/ModifySelection.cs @@ -1,6 +1,5 @@ using Nikse.SubtitleEdit.Controls; using Nikse.SubtitleEdit.Core; -using Nikse.SubtitleEdit.Logic; using System; using System.Text.RegularExpressions; using System.Windows.Forms; diff --git a/src/Forms/MultipleReplace.cs b/src/Forms/MultipleReplace.cs index f0fb2c05f..3da5f07b2 100644 --- a/src/Forms/MultipleReplace.cs +++ b/src/Forms/MultipleReplace.cs @@ -1,5 +1,4 @@ using Nikse.SubtitleEdit.Core; -using Nikse.SubtitleEdit.Logic; using System; using System.Collections.Generic; using System.Globalization; diff --git a/src/Forms/NetworkChat.cs b/src/Forms/NetworkChat.cs index 716fbc7d8..6da2fb93d 100644 --- a/src/Forms/NetworkChat.cs +++ b/src/Forms/NetworkChat.cs @@ -1,8 +1,7 @@ -using System; -using System.Windows.Forms; -using Nikse.SubtitleEdit.Core; -using Nikse.SubtitleEdit.Logic; +using Nikse.SubtitleEdit.Core; using Nikse.SubtitleEdit.Logic.Networking; +using System; +using System.Windows.Forms; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/NetworkJoin.cs b/src/Forms/NetworkJoin.cs index 10bcd180d..349c6fc4a 100644 --- a/src/Forms/NetworkJoin.cs +++ b/src/Forms/NetworkJoin.cs @@ -1,7 +1,7 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.Net; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/NetworkLogAndInfo.cs b/src/Forms/NetworkLogAndInfo.cs index 0953077cd..15a901d8f 100644 --- a/src/Forms/NetworkLogAndInfo.cs +++ b/src/Forms/NetworkLogAndInfo.cs @@ -1,6 +1,6 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/NetworkStart.cs b/src/Forms/NetworkStart.cs index f7be61baa..919b9ef76 100644 --- a/src/Forms/NetworkStart.cs +++ b/src/Forms/NetworkStart.cs @@ -1,7 +1,7 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.Net; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/NuendoProperties.cs b/src/Forms/NuendoProperties.cs index 24257786f..9d76163b1 100644 --- a/src/Forms/NuendoProperties.cs +++ b/src/Forms/NuendoProperties.cs @@ -1,7 +1,7 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.Collections.Generic; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/OCRSpellCheck.cs b/src/Forms/OCRSpellCheck.cs index 771a2ffbe..9df6f9c21 100644 --- a/src/Forms/OCRSpellCheck.cs +++ b/src/Forms/OCRSpellCheck.cs @@ -1,9 +1,8 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; -using Nikse.SubtitleEdit.Core; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/OpenVideoDvd.cs b/src/Forms/OpenVideoDvd.cs index 268a76f9b..61812824e 100644 --- a/src/Forms/OpenVideoDvd.cs +++ b/src/Forms/OpenVideoDvd.cs @@ -1,5 +1,4 @@ using Nikse.SubtitleEdit.Core; -using Nikse.SubtitleEdit.Logic; using System; using System.IO; using System.Windows.Forms; diff --git a/src/Forms/PacEncoding.cs b/src/Forms/PacEncoding.cs index 78c5aeaef..544903f73 100644 --- a/src/Forms/PacEncoding.cs +++ b/src/Forms/PacEncoding.cs @@ -1,8 +1,8 @@ -using System; +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.SubtitleFormats; +using System; using System.Text; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.SubtitleFormats; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/PluginsGet.cs b/src/Forms/PluginsGet.cs index d27e4b017..61a1629a7 100644 --- a/src/Forms/PluginsGet.cs +++ b/src/Forms/PluginsGet.cs @@ -1,10 +1,9 @@ -using Nikse.SubtitleEdit.Logic; +using Nikse.SubtitleEdit.Core; using System; using System.Collections.Generic; using System.Drawing; using System.Globalization; using System.IO; -using System.IO.Compression; using System.Net; using System.Windows.Forms; using System.Xml; diff --git a/src/Forms/RemoveTextFromHearImpaired.cs b/src/Forms/RemoveTextFromHearImpaired.cs index 00dcb4307..ffe02cde2 100644 --- a/src/Forms/RemoveTextFromHearImpaired.cs +++ b/src/Forms/RemoveTextFromHearImpaired.cs @@ -1,10 +1,10 @@ -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.Forms; +using Nikse.SubtitleEdit.Core; using System; using System.Collections.Generic; using System.Drawing; using System.Globalization; using System.Windows.Forms; +using Nikse.SubtitleEdit.Core.Forms; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/ReplaceDialog.cs b/src/Forms/ReplaceDialog.cs index bc20c39a0..42e0a262c 100644 --- a/src/Forms/ReplaceDialog.cs +++ b/src/Forms/ReplaceDialog.cs @@ -1,9 +1,9 @@ -using System; +using Nikse.Core.Enums; +using Nikse.SubtitleEdit.Core; +using System; using System.Drawing; using System.Text.RegularExpressions; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.Enums; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/RestoreAutoBackup.cs b/src/Forms/RestoreAutoBackup.cs index e95f6578c..a65b653d6 100644 --- a/src/Forms/RestoreAutoBackup.cs +++ b/src/Forms/RestoreAutoBackup.cs @@ -1,8 +1,8 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.IO; using System.Text.RegularExpressions; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/SeekSilence.cs b/src/Forms/SeekSilence.cs index 4b5f7c71d..0db939ba7 100644 --- a/src/Forms/SeekSilence.cs +++ b/src/Forms/SeekSilence.cs @@ -1,6 +1,6 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/SetMinimumDisplayTimeBetweenParagraphs.cs b/src/Forms/SetMinimumDisplayTimeBetweenParagraphs.cs index ac6f417d6..bb60bd020 100644 --- a/src/Forms/SetMinimumDisplayTimeBetweenParagraphs.cs +++ b/src/Forms/SetMinimumDisplayTimeBetweenParagraphs.cs @@ -1,9 +1,9 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.Collections.Generic; using System.Drawing; -using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; using System.Globalization; +using System.Windows.Forms; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/SetSyncPoint.cs b/src/Forms/SetSyncPoint.cs index 728e5c00c..bcc99c79d 100644 --- a/src/Forms/SetSyncPoint.cs +++ b/src/Forms/SetSyncPoint.cs @@ -1,9 +1,9 @@ -using System; -using System.IO; -using System.Windows.Forms; -using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core; using Nikse.SubtitleEdit.Logic; using Nikse.SubtitleEdit.Logic.VideoPlayers; +using System; +using System.IO; +using System.Windows.Forms; namespace Nikse.SubtitleEdit.Forms { @@ -135,9 +135,9 @@ namespace Nikse.SubtitleEdit.Forms videoPlayerContainer1.VideoPlayer.DisposeVideoPlayer(); } - VideoInfo videoInfo = Utilities.GetVideoInfo(fileName); + VideoInfo videoInfo = UiUtil.GetVideoInfo(fileName); - Utilities.InitializeVideoPlayerAndContainer(fileName, videoInfo, videoPlayerContainer1, VideoStartLoaded, VideoStartEnded); + UiUtil.InitializeVideoPlayerAndContainer(fileName, videoInfo, videoPlayerContainer1, VideoStartLoaded, VideoStartEnded); } } @@ -189,7 +189,7 @@ namespace Nikse.SubtitleEdit.Forms } if (pos != _lastPosition) { - Utilities.ShowSubtitle(_subtitle.Paragraphs, videoPlayerContainer1); + UiUtil.ShowSubtitle(_subtitle.Paragraphs, videoPlayerContainer1); timeUpDownLine.TimeCode = TimeCode.FromSeconds(pos); _lastPosition = pos; } diff --git a/src/Forms/SetVideoOffset.cs b/src/Forms/SetVideoOffset.cs index 028412d1c..00623f9c8 100644 --- a/src/Forms/SetVideoOffset.cs +++ b/src/Forms/SetVideoOffset.cs @@ -1,4 +1,4 @@ -using Nikse.SubtitleEdit.Logic; +using Nikse.SubtitleEdit.Core; using System; using System.Windows.Forms; diff --git a/src/Forms/Settings.cs b/src/Forms/Settings.cs index a06cace9a..1e5077aa6 100644 --- a/src/Forms/Settings.cs +++ b/src/Forms/Settings.cs @@ -1,6 +1,6 @@ using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.Dictionaries; using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.Dictionaries; using Nikse.SubtitleEdit.Logic.VideoPlayers; using System; using System.Collections.Generic; @@ -121,22 +121,22 @@ namespace Nikse.SubtitleEdit.Forms if (gs.VideoPlayer.Trim().Equals("VLC", StringComparison.OrdinalIgnoreCase) && LibVlcDynamic.IsInstalled) radioButtonVideoPlayerVLC.Checked = true; - else if (gs.VideoPlayer.Trim().Equals("MPlayer", StringComparison.OrdinalIgnoreCase) && Utilities.IsMPlayerAvailable) + else if (gs.VideoPlayer.Trim().Equals("MPlayer", StringComparison.OrdinalIgnoreCase) && UiUtil.IsMPlayerAvailable) radioButtonVideoPlayerMPlayer.Checked = true; - else if (gs.VideoPlayer.Trim().Equals("MPC-HC", StringComparison.OrdinalIgnoreCase) && Utilities.IsMpcHcInstalled) + else if (gs.VideoPlayer.Trim().Equals("MPC-HC", StringComparison.OrdinalIgnoreCase) && UiUtil.IsMpcHcInstalled) radioButtonVideoPlayerMpcHc.Checked = true; - else if (Utilities.IsQuartsDllInstalled) + else if (UiUtil.IsQuartsDllInstalled) radioButtonVideoPlayerDirectShow.Checked = true; - else if (Utilities.IsMPlayerAvailable) + else if (UiUtil.IsMPlayerAvailable) radioButtonVideoPlayerMPlayer.Checked = true; else if (LibVlcDynamic.IsInstalled) radioButtonVideoPlayerVLC.Checked = true; if (!LibVlcDynamic.IsInstalled) radioButtonVideoPlayerVLC.Enabled = false; - if (!Utilities.IsMPlayerAvailable) + if (!UiUtil.IsMPlayerAvailable) radioButtonVideoPlayerMPlayer.Enabled = false; - if (!Utilities.IsQuartsDllInstalled) + if (!UiUtil.IsQuartsDllInstalled) radioButtonVideoPlayerDirectShow.Enabled = false; if (Logic.VideoPlayers.MpcHC.MpcHc.GetMpcHcFileName() == null) radioButtonVideoPlayerMpcHc.Enabled = false; diff --git a/src/Forms/ShowEarlierLater.cs b/src/Forms/ShowEarlierLater.cs index 84eaa3e97..61479adc6 100644 --- a/src/Forms/ShowEarlierLater.cs +++ b/src/Forms/ShowEarlierLater.cs @@ -1,7 +1,7 @@ -using System; +using Nikse.Core.Enums; +using Nikse.SubtitleEdit.Core; +using System; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.Enums; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/ShowHistory.cs b/src/Forms/ShowHistory.cs index c3341058d..5fdac8276 100644 --- a/src/Forms/ShowHistory.cs +++ b/src/Forms/ShowHistory.cs @@ -1,7 +1,7 @@ -using System; -using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; +using Nikse.SubtitleEdit.Core; +using System; using System.Drawing; +using System.Windows.Forms; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/SpellCheck.cs b/src/Forms/SpellCheck.cs index caf0a4213..32daaad8c 100644 --- a/src/Forms/SpellCheck.cs +++ b/src/Forms/SpellCheck.cs @@ -1,7 +1,6 @@ -using Nikse.SubtitleEdit.Core; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.Dictionaries; -using Nikse.SubtitleEdit.Logic.Enums; +using Nikse.Core.Enums; +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.Dictionaries; using Nikse.SubtitleEdit.Logic.SpellCheck; using System; using System.Collections.Generic; diff --git a/src/Forms/Split.cs b/src/Forms/Split.cs index 0ed3f6a64..b175ca281 100644 --- a/src/Forms/Split.cs +++ b/src/Forms/Split.cs @@ -1,10 +1,10 @@ -using System; +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.SubtitleFormats; +using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.SubtitleFormats; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/SplitLongLines.cs b/src/Forms/SplitLongLines.cs index 39ec19cfb..b2814646d 100644 --- a/src/Forms/SplitLongLines.cs +++ b/src/Forms/SplitLongLines.cs @@ -1,11 +1,10 @@ using Nikse.SubtitleEdit.Core; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.Forms; using System; using System.Collections.Generic; using System.Drawing; using System.Globalization; using System.Windows.Forms; +using Nikse.SubtitleEdit.Core.Forms; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/SplitSubtitle.cs b/src/Forms/SplitSubtitle.cs index d8ba1103a..e1882afc6 100644 --- a/src/Forms/SplitSubtitle.cs +++ b/src/Forms/SplitSubtitle.cs @@ -1,9 +1,10 @@ -using System; +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.SubtitleFormats; +using Nikse.SubtitleEdit.Logic; +using System; using System.IO; using System.Text; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.SubtitleFormats; namespace Nikse.SubtitleEdit.Forms { @@ -146,7 +147,7 @@ namespace Nikse.SubtitleEdit.Forms openFileDialog1.FileName = string.Empty; if (openFileDialog1.ShowDialog() == DialogResult.OK) { - VideoInfo info = Utilities.GetVideoInfo(openFileDialog1.FileName); + VideoInfo info = UiUtil.GetVideoInfo(openFileDialog1.FileName); if (info != null && info.Success) { splitTimeUpDownAdjust.TimeCode = new TimeCode(info.TotalMilliseconds); diff --git a/src/Forms/StartNumberingFrom.cs b/src/Forms/StartNumberingFrom.cs index 44a2b3034..0b60762f9 100644 --- a/src/Forms/StartNumberingFrom.cs +++ b/src/Forms/StartNumberingFrom.cs @@ -1,6 +1,6 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/Statistics.cs b/src/Forms/Statistics.cs index 23b196657..93333759f 100644 --- a/src/Forms/Statistics.cs +++ b/src/Forms/Statistics.cs @@ -1,11 +1,10 @@ -using System; +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.SubtitleFormats; +using System; using System.Collections.Generic; using System.Globalization; using System.Text; using System.Windows.Forms; -using Nikse.SubtitleEdit.Core; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.SubtitleFormats; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/StatusLog.cs b/src/Forms/StatusLog.cs index 3f1de5b8c..8ae99eeea 100644 --- a/src/Forms/StatusLog.cs +++ b/src/Forms/StatusLog.cs @@ -1,5 +1,5 @@ -using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; +using Nikse.SubtitleEdit.Core; +using System.Windows.Forms; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/Styles/StylesForm.cs b/src/Forms/Styles/StylesForm.cs index 114623a76..a044d0f1c 100644 --- a/src/Forms/Styles/StylesForm.cs +++ b/src/Forms/Styles/StylesForm.cs @@ -1,6 +1,7 @@ using System; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; +using Nikse.Core; +using Nikse.SubtitleEdit.Core; namespace Nikse.SubtitleEdit.Forms.Styles { diff --git a/src/Forms/Styles/SubStationAlphaStyles.cs b/src/Forms/Styles/SubStationAlphaStyles.cs index ebf985ce3..db40eadcc 100644 --- a/src/Forms/Styles/SubStationAlphaStyles.cs +++ b/src/Forms/Styles/SubStationAlphaStyles.cs @@ -7,8 +7,9 @@ using System.IO; using System.Text; using System.Windows.Forms; using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.SubtitleFormats; using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.SubtitleFormats; +using Nikse.Core; namespace Nikse.SubtitleEdit.Forms.Styles { diff --git a/src/Forms/Styles/TimedTextStyles.cs b/src/Forms/Styles/TimedTextStyles.cs index 932746418..1e856b7d1 100644 --- a/src/Forms/Styles/TimedTextStyles.cs +++ b/src/Forms/Styles/TimedTextStyles.cs @@ -5,9 +5,9 @@ using System.Drawing.Text; using System.Text; using System.Windows.Forms; using System.Xml; +using Nikse.Core; using Nikse.SubtitleEdit.Core; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.SubtitleFormats; +using Nikse.SubtitleEdit.Core.SubtitleFormats; namespace Nikse.SubtitleEdit.Forms.Styles { diff --git a/src/Forms/SubStationAlphaProperties.cs b/src/Forms/SubStationAlphaProperties.cs index a2a31e874..8be68ad5d 100644 --- a/src/Forms/SubStationAlphaProperties.cs +++ b/src/Forms/SubStationAlphaProperties.cs @@ -1,11 +1,11 @@ -using System; +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.SubtitleFormats; +using Nikse.SubtitleEdit.Logic; +using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Windows.Forms; -using Nikse.SubtitleEdit.Core; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.SubtitleFormats; namespace Nikse.SubtitleEdit.Forms { @@ -251,7 +251,7 @@ namespace Nikse.SubtitleEdit.Forms if (openFileDialog1.ShowDialog() == DialogResult.OK) { - VideoInfo info = Utilities.GetVideoInfo(openFileDialog1.FileName); + VideoInfo info = UiUtil.GetVideoInfo(openFileDialog1.FileName); if (info != null && info.Success) { numericUpDownVideoWidth.Value = info.Width; diff --git a/src/Forms/SyncPointsSync.cs b/src/Forms/SyncPointsSync.cs index 54d4f6707..d28111a35 100644 --- a/src/Forms/SyncPointsSync.cs +++ b/src/Forms/SyncPointsSync.cs @@ -1,9 +1,9 @@ -using System.Linq; -using Nikse.SubtitleEdit.Logic; +using Nikse.SubtitleEdit.Core; using System; using System.Collections.Generic; using System.Drawing; using System.Globalization; +using System.Linq; using System.Windows.Forms; namespace Nikse.SubtitleEdit.Forms diff --git a/src/Forms/TimedTextProperties.cs b/src/Forms/TimedTextProperties.cs index 0231cb0c8..09b15cc4e 100644 --- a/src/Forms/TimedTextProperties.cs +++ b/src/Forms/TimedTextProperties.cs @@ -1,9 +1,9 @@ -using System; +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.SubtitleFormats; +using System; using System.Globalization; using System.Windows.Forms; using System.Xml; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.SubtitleFormats; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/TransportStreamSubtitleChooser.cs b/src/Forms/TransportStreamSubtitleChooser.cs index 1ba7f7aa6..785cb38bb 100644 --- a/src/Forms/TransportStreamSubtitleChooser.cs +++ b/src/Forms/TransportStreamSubtitleChooser.cs @@ -1,4 +1,5 @@ -using Nikse.SubtitleEdit.Logic; +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.TransportStream; using System; using System.Windows.Forms; @@ -6,7 +7,7 @@ namespace Nikse.SubtitleEdit.Forms { public partial class TransportStreamSubtitleChooser : PositionAndSizeForm { - private Logic.TransportStream.TransportStreamParser _tsParser; + private TransportStreamParser _tsParser; public TransportStreamSubtitleChooser() { @@ -33,7 +34,7 @@ namespace Nikse.SubtitleEdit.Forms DialogResult = DialogResult.Cancel; } - internal void Initialize(Logic.TransportStream.TransportStreamParser tsParser, string fileName) + internal void Initialize(TransportStreamParser tsParser, string fileName) { _tsParser = tsParser; Text = string.Format(Configuration.Settings.Language.TransportStreamSubtitleChooser.Title, fileName); diff --git a/src/Forms/UnknownSubtitle.cs b/src/Forms/UnknownSubtitle.cs index 5e62fc248..7d74a7d14 100644 --- a/src/Forms/UnknownSubtitle.cs +++ b/src/Forms/UnknownSubtitle.cs @@ -1,5 +1,5 @@ -using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; +using Nikse.SubtitleEdit.Core; +using System.Windows.Forms; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/VideoControlsUndocked.cs b/src/Forms/VideoControlsUndocked.cs index 7c36132a1..37b688712 100644 --- a/src/Forms/VideoControlsUndocked.cs +++ b/src/Forms/VideoControlsUndocked.cs @@ -1,6 +1,6 @@ -using System.Drawing; +using Nikse.SubtitleEdit.Core; +using System.Drawing; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/VideoError.cs b/src/Forms/VideoError.cs index 248910836..b8be90688 100644 --- a/src/Forms/VideoError.cs +++ b/src/Forms/VideoError.cs @@ -1,9 +1,9 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.Diagnostics; using System.IO; using System.Text; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/VideoPlayerUndocked.cs b/src/Forms/VideoPlayerUndocked.cs index fba3b7789..e0a3ff85e 100644 --- a/src/Forms/VideoPlayerUndocked.cs +++ b/src/Forms/VideoPlayerUndocked.cs @@ -1,7 +1,7 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.Drawing; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/VisualSync.cs b/src/Forms/VisualSync.cs index 7e0b814e1..b9e7ef740 100644 --- a/src/Forms/VisualSync.cs +++ b/src/Forms/VisualSync.cs @@ -1,12 +1,12 @@ -using System; +using Nikse.SubtitleEdit.Controls; +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Logic; +using Nikse.SubtitleEdit.Logic.VideoPlayers; +using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Windows.Forms; -using Nikse.SubtitleEdit.Controls; -using Nikse.SubtitleEdit.Core; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.VideoPlayers; namespace Nikse.SubtitleEdit.Forms { @@ -156,7 +156,7 @@ namespace Nikse.SubtitleEdit.Forms } } - Utilities.InitializeVideoPlayerAndContainer(fileName, videoInfo, MediaPlayerStart, VideoStartLoaded, VideoStartEnded); + UiUtil.InitializeVideoPlayerAndContainer(fileName, videoInfo, MediaPlayerStart, VideoStartLoaded, VideoStartEnded); } } @@ -180,7 +180,7 @@ namespace Nikse.SubtitleEdit.Forms } else { - Utilities.InitializeVideoPlayerAndContainer(MediaPlayerStart.VideoPlayer.VideoFileName, _videoInfo, MediaPlayerEnd, VideoEndLoaded, VideoEndEnded); + UiUtil.InitializeVideoPlayerAndContainer(MediaPlayerStart.VideoPlayer.VideoFileName, _videoInfo, MediaPlayerEnd, VideoEndLoaded, VideoEndEnded); } timer1.Start(); timerProgressBarRefresh.Start(); @@ -215,7 +215,7 @@ namespace Nikse.SubtitleEdit.Forms private VideoInfo ShowVideoInfo(string fileName) { - _videoInfo = Utilities.GetVideoInfo(fileName); + _videoInfo = UiUtil.GetVideoInfo(fileName); var info = new FileInfo(fileName); long fileSizeInBytes = info.Length; @@ -244,7 +244,7 @@ namespace Nikse.SubtitleEdit.Forms MediaPlayerStart.CurrentPosition = _startGoBackPosition; _startStopPosition = -1; } - Utilities.ShowSubtitle(_paragraphs, MediaPlayerStart); + UiUtil.ShowSubtitle(_paragraphs, MediaPlayerStart); } if (!MediaPlayerEnd.IsPaused) { @@ -255,7 +255,7 @@ namespace Nikse.SubtitleEdit.Forms MediaPlayerEnd.CurrentPosition = _endGoBackPosition; _endStopPosition = -1; } - Utilities.ShowSubtitle(_paragraphs, MediaPlayerEnd); + UiUtil.ShowSubtitle(_paragraphs, MediaPlayerEnd); } } } @@ -445,7 +445,7 @@ namespace Nikse.SubtitleEdit.Forms mediaPlayer.CurrentPosition -= seconds; else mediaPlayer.CurrentPosition = 0; - Utilities.ShowSubtitle(_paragraphs, mediaPlayer); + UiUtil.ShowSubtitle(_paragraphs, mediaPlayer); } private void ButtonStartHalfASecondBackClick(object sender, EventArgs e) diff --git a/src/Forms/VobSubEditCharacters.cs b/src/Forms/VobSubEditCharacters.cs index 7a1597c19..a8d0fab29 100644 --- a/src/Forms/VobSubEditCharacters.cs +++ b/src/Forms/VobSubEditCharacters.cs @@ -1,12 +1,11 @@ -using System; +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Logic.Ocr.Binary; +using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Windows.Forms; using System.Xml; -using Nikse.SubtitleEdit.Core; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.Ocr.Binary; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/VobSubNOcrCharacterInspect.cs b/src/Forms/VobSubNOcrCharacterInspect.cs index c1ef6dd23..0aff96861 100644 --- a/src/Forms/VobSubNOcrCharacterInspect.cs +++ b/src/Forms/VobSubNOcrCharacterInspect.cs @@ -1,4 +1,5 @@ -using Nikse.SubtitleEdit.Logic; +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Logic; using Nikse.SubtitleEdit.Logic.Ocr; using System; using System.Collections.Generic; diff --git a/src/Forms/VobSubNOcrEdit.cs b/src/Forms/VobSubNOcrEdit.cs index ff0887e04..f008885da 100644 --- a/src/Forms/VobSubNOcrEdit.cs +++ b/src/Forms/VobSubNOcrEdit.cs @@ -1,4 +1,4 @@ -using Nikse.SubtitleEdit.Logic; +using Nikse.SubtitleEdit.Core; using Nikse.SubtitleEdit.Logic.Ocr; using System; using System.Collections.Generic; diff --git a/src/Forms/VobSubNOcrTrain.cs b/src/Forms/VobSubNOcrTrain.cs index 9f8161e26..d0af13b8e 100644 --- a/src/Forms/VobSubNOcrTrain.cs +++ b/src/Forms/VobSubNOcrTrain.cs @@ -1,7 +1,7 @@ using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.SubtitleFormats; using Nikse.SubtitleEdit.Logic; using Nikse.SubtitleEdit.Logic.Ocr; -using Nikse.SubtitleEdit.Logic.SubtitleFormats; using System; using System.Collections.Generic; using System.Drawing; diff --git a/src/Forms/VobSubOcr.cs b/src/Forms/VobSubOcr.cs index 537851821..1b5ed8b57 100644 --- a/src/Forms/VobSubOcr.cs +++ b/src/Forms/VobSubOcr.cs @@ -1,10 +1,12 @@ -using Nikse.SubtitleEdit.Core; +using Nikse.Core; +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.BluRaySup; +using Nikse.SubtitleEdit.Core.SubtitleFormats; +using Nikse.SubtitleEdit.Core.TransportStream; +using Nikse.SubtitleEdit.Core.VobSub; using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.BluRaySup; using Nikse.SubtitleEdit.Logic.Ocr; using Nikse.SubtitleEdit.Logic.Ocr.Binary; -using Nikse.SubtitleEdit.Logic.SubtitleFormats; -using Nikse.SubtitleEdit.Logic.VobSub; using System; using System.Collections.Generic; using System.ComponentModel; @@ -256,7 +258,7 @@ namespace Nikse.SubtitleEdit.Forms private List _xSubList; // DVB (from transport stream) - private List _dvbSubtitles; + private List _dvbSubtitles; private Color _dvbSubColor = Color.Transparent; private string _lastLine; @@ -8291,7 +8293,7 @@ namespace Nikse.SubtitleEdit.Forms } // TODO: Get language from ts file - internal void Initialize(List subtitles, VobSubOcrSettings vobSubOcrSettings, string fileName) + internal void Initialize(List subtitles, VobSubOcrSettings vobSubOcrSettings, string fileName) { buttonOK.Enabled = false; buttonCancel.Enabled = false; @@ -8378,14 +8380,14 @@ namespace Nikse.SubtitleEdit.Forms private void SplitDvbForEachSubImage() { - var list = new List(); + var list = new List(); foreach (var dvbSub in _dvbSubtitles) { if (dvbSub.ActiveImageIndex == null) { for (int i = 0; i < dvbSub.Pes.ObjectDataList.Count; i++) { - var newDbvSub = new Logic.TransportStream.TransportStreamSubtitle(); + var newDbvSub = new TransportStreamSubtitle(); newDbvSub.Pes = dvbSub.Pes; newDbvSub.ActiveImageIndex = i; newDbvSub.StartMilliseconds = dvbSub.StartMilliseconds; diff --git a/src/Forms/VobSubOcrCharacter.cs b/src/Forms/VobSubOcrCharacter.cs index 2d0747f01..01313a98b 100644 --- a/src/Forms/VobSubOcrCharacter.cs +++ b/src/Forms/VobSubOcrCharacter.cs @@ -1,8 +1,8 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; +using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; -using System.Collections.Generic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/VobSubOcrCharacterInspect.cs b/src/Forms/VobSubOcrCharacterInspect.cs index 2a28ab2e9..9ebe64e59 100644 --- a/src/Forms/VobSubOcrCharacterInspect.cs +++ b/src/Forms/VobSubOcrCharacterInspect.cs @@ -1,12 +1,12 @@ -using System; +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Logic.Ocr.Binary; +using System; using System.Collections.Generic; using System.Drawing; using System.Globalization; using System.IO; using System.Windows.Forms; using System.Xml; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.Ocr.Binary; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/VobSubOcrNOcrCharacter.cs b/src/Forms/VobSubOcrNOcrCharacter.cs index 4ee623b92..4371c8855 100644 --- a/src/Forms/VobSubOcrNOcrCharacter.cs +++ b/src/Forms/VobSubOcrNOcrCharacter.cs @@ -1,9 +1,9 @@ -using System; +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Logic.Ocr; +using System; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.Ocr; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/VobSubOcrNewFolder.cs b/src/Forms/VobSubOcrNewFolder.cs index abb9e4223..9fe50fb2a 100644 --- a/src/Forms/VobSubOcrNewFolder.cs +++ b/src/Forms/VobSubOcrNewFolder.cs @@ -1,8 +1,7 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.IO; using System.Windows.Forms; -using Nikse.SubtitleEdit.Core; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/VobSubOcrSetItalicFactor.cs b/src/Forms/VobSubOcrSetItalicFactor.cs index 608a6302d..de24ef2c3 100644 --- a/src/Forms/VobSubOcrSetItalicFactor.cs +++ b/src/Forms/VobSubOcrSetItalicFactor.cs @@ -1,7 +1,7 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.Drawing; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/Watermark.cs b/src/Forms/Watermark.cs index f1b15f7a7..ced6b9e0c 100644 --- a/src/Forms/Watermark.cs +++ b/src/Forms/Watermark.cs @@ -1,10 +1,9 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; +using System.Collections.Generic; using System.Globalization; using System.Text; using System.Windows.Forms; -using Nikse.SubtitleEdit.Core; -using Nikse.SubtitleEdit.Logic; -using System.Collections.Generic; namespace Nikse.SubtitleEdit.Forms { @@ -21,9 +20,9 @@ namespace Nikse.SubtitleEdit.Forms Utilities.FixLargeFonts(this, buttonOK); } - internal void Initialize(Logic.Subtitle subtitle, int firstSelectedIndex) + internal void Initialize(Subtitle subtitle, int firstSelectedIndex) { - StringBuilder sb = new StringBuilder(); + var sb = new StringBuilder(); foreach (Paragraph p in subtitle.Paragraphs) sb.AppendLine(p.Text); diff --git a/src/Forms/WaveformGenerateTimeCodes.cs b/src/Forms/WaveformGenerateTimeCodes.cs index 55a2b78b2..a32e84cd9 100644 --- a/src/Forms/WaveformGenerateTimeCodes.cs +++ b/src/Forms/WaveformGenerateTimeCodes.cs @@ -1,6 +1,6 @@ -using System; +using Nikse.SubtitleEdit.Core; +using System; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/WaveformUndocked.cs b/src/Forms/WaveformUndocked.cs index 217a9c4eb..06271a7f4 100644 --- a/src/Forms/WaveformUndocked.cs +++ b/src/Forms/WaveformUndocked.cs @@ -1,6 +1,6 @@ -using System.Drawing; +using Nikse.SubtitleEdit.Core; +using System.Drawing; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/WebVttNewVoice.cs b/src/Forms/WebVttNewVoice.cs index 3069c0e31..e0be51d43 100644 --- a/src/Forms/WebVttNewVoice.cs +++ b/src/Forms/WebVttNewVoice.cs @@ -1,7 +1,6 @@ -using System; -using System.Drawing; +using Nikse.SubtitleEdit.Core; +using System; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Forms/YouTubeAnnotationsImport.cs b/src/Forms/YouTubeAnnotationsImport.cs index 017393092..95229be55 100644 --- a/src/Forms/YouTubeAnnotationsImport.cs +++ b/src/Forms/YouTubeAnnotationsImport.cs @@ -1,7 +1,6 @@ -using System.Collections.Generic; -using System.Drawing; +using Nikse.SubtitleEdit.Core; +using System.Collections.Generic; using System.Windows.Forms; -using Nikse.SubtitleEdit.Logic; namespace Nikse.SubtitleEdit.Forms { diff --git a/src/Logic/CommandLineConvert.cs b/src/Logic/CommandLineConvert.cs index 004b72d25..fbe4012f7 100644 --- a/src/Logic/CommandLineConvert.cs +++ b/src/Logic/CommandLineConvert.cs @@ -1,13 +1,12 @@ -using System.Windows.Forms.VisualStyles; -using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.ContainerFormats.Matroska; +using Nikse.SubtitleEdit.Core.SubtitleFormats; using Nikse.SubtitleEdit.Forms; -using Nikse.SubtitleEdit.Logic.BluRaySup; -using Nikse.SubtitleEdit.Logic.ContainerFormats.Matroska; -using Nikse.SubtitleEdit.Logic.SubtitleFormats; using System; using System.Collections.Generic; using System.IO; using System.Text; +using Nikse.SubtitleEdit.Core.BluRaySup; namespace Nikse.SubtitleEdit.Logic { @@ -644,7 +643,7 @@ namespace Nikse.SubtitleEdit.Logic targetFormatFound = true; outputFileName = FormatOutputFileNameForBatchConvert(fileName, pac.Extension, outputFolder, overwrite); Console.Write("{0}: {1} -> {2}...", count, Path.GetFileName(fileName), outputFileName); - pac.Save(outputFileName, sub); + pac.Save(outputFileName, sub, null); Console.WriteLine(" done."); } } diff --git a/src/Logic/Dictionaries/OcrFixReplaceList.cs b/src/Logic/Dictionaries/OcrFixReplaceList.cs index 442ae05e0..4d694eb09 100644 --- a/src/Logic/Dictionaries/OcrFixReplaceList.cs +++ b/src/Logic/Dictionaries/OcrFixReplaceList.cs @@ -5,6 +5,7 @@ using System.Text; using System.Text.RegularExpressions; using System.Windows.Forms; using System.Xml; +using Nikse.Core; using Nikse.SubtitleEdit.Core; namespace Nikse.SubtitleEdit.Logic.Dictionaries diff --git a/src/Logic/Forms/FixCommonErrorsHelper.cs b/src/Logic/Forms/FixCommonErrorsHelper.cs index 59cde3b79..24ce4b41e 100644 --- a/src/Logic/Forms/FixCommonErrorsHelper.cs +++ b/src/Logic/Forms/FixCommonErrorsHelper.cs @@ -1,6 +1,9 @@ using Nikse.SubtitleEdit.Core; using System; using System.Linq; +using Paragraph = Nikse.SubtitleEdit.Core.Paragraph; +using Subtitle = Nikse.SubtitleEdit.Core.Subtitle; +using Utilities = Nikse.SubtitleEdit.Core.Utilities; namespace Nikse.SubtitleEdit.Logic.Forms { diff --git a/src/Logic/Forms/RemoveTextForHISettings.cs b/src/Logic/Forms/RemoveTextForHISettings.cs index c6e4f43b1..c317bb9cf 100644 --- a/src/Logic/Forms/RemoveTextForHISettings.cs +++ b/src/Logic/Forms/RemoveTextForHISettings.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Nikse.SubtitleEdit.Core; namespace Nikse.SubtitleEdit.Logic.Forms { diff --git a/src/Logic/Forms/SplitLongLinesHelper.cs b/src/Logic/Forms/SplitLongLinesHelper.cs index 9729f1d0f..bf9fa37d0 100644 --- a/src/Logic/Forms/SplitLongLinesHelper.cs +++ b/src/Logic/Forms/SplitLongLinesHelper.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Nikse.SubtitleEdit.Core; +using Nikse.Core; namespace Nikse.SubtitleEdit.Logic.Forms { diff --git a/src/Logic/Networking/NikseWebServiceSession.cs b/src/Logic/Networking/NikseWebServiceSession.cs index f1b51ee8f..2183a3533 100644 --- a/src/Logic/Networking/NikseWebServiceSession.cs +++ b/src/Logic/Networking/NikseWebServiceSession.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.Net; using System.Text; +using Nikse.Core; +using Nikse.SubtitleEdit.Core; namespace Nikse.SubtitleEdit.Logic.Networking { diff --git a/src/Logic/NikseBitmapImageSplitter.cs b/src/Logic/NikseBitmapImageSplitter.cs index 722119aa3..fdde2724e 100644 --- a/src/Logic/NikseBitmapImageSplitter.cs +++ b/src/Logic/NikseBitmapImageSplitter.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Drawing; +using Nikse.Core; +using Nikse.SubtitleEdit.Core; namespace Nikse.SubtitleEdit.Logic { diff --git a/src/Logic/OCR/Binary/BinaryOcrBitmap.cs b/src/Logic/OCR/Binary/BinaryOcrBitmap.cs index a23b42a15..c0af661aa 100644 --- a/src/Logic/OCR/Binary/BinaryOcrBitmap.cs +++ b/src/Logic/OCR/Binary/BinaryOcrBitmap.cs @@ -2,6 +2,9 @@ using System.Collections.Generic; using System.Drawing; using System.IO; +using Nikse.Core; +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.VobSub; namespace Nikse.SubtitleEdit.Logic.Ocr.Binary { @@ -73,8 +76,8 @@ namespace Nikse.SubtitleEdit.Logic.Ocr.Binary X = buffer[4] << 8 | buffer[5]; Y = buffer[6] << 8 | buffer[7]; NumberOfColoredPixels = buffer[8] << 8 | buffer[9]; - Italic = (buffer[10] & VobSub.Helper.B10000000) > 0; - ExpandCount = buffer[10] & VobSub.Helper.B01111111; + Italic = (buffer[10] & Helper.B10000000) > 0; + ExpandCount = buffer[10] & Helper.B01111111; Hash = (uint)(buffer[11] << 24 | buffer[12] << 16 | buffer[13] << 8 | buffer[14]); int textLen = buffer[15]; if (textLen > 0) @@ -145,9 +148,9 @@ namespace Nikse.SubtitleEdit.Logic.Ocr.Binary WriteInt16(stream, (ushort)NumberOfColoredPixels); - byte flags = (byte)(ExpandCount & VobSub.Helper.B01111111); + byte flags = (byte)(ExpandCount & Helper.B01111111); if (Italic) - flags = (byte)(flags + VobSub.Helper.B10000000); + flags = (byte)(flags + Helper.B10000000); stream.WriteByte(flags); WriteInt32(stream, Hash); diff --git a/src/Logic/OCR/NOcrDb.cs b/src/Logic/OCR/NOcrDb.cs index 70c1c2f60..74550adc9 100644 --- a/src/Logic/OCR/NOcrDb.cs +++ b/src/Logic/OCR/NOcrDb.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.Drawing; using System.IO; using System.IO.Compression; +using Nikse.Core; +using Nikse.SubtitleEdit.Core; namespace Nikse.SubtitleEdit.Logic.Ocr { diff --git a/src/Logic/OCR/OcrFixEngine.cs b/src/Logic/OCR/OcrFixEngine.cs index 820bdf74c..5987e0c6e 100644 --- a/src/Logic/OCR/OcrFixEngine.cs +++ b/src/Logic/OCR/OcrFixEngine.cs @@ -1,6 +1,6 @@ using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.Dictionaries; using Nikse.SubtitleEdit.Forms; -using Nikse.SubtitleEdit.Logic.Dictionaries; using Nikse.SubtitleEdit.Logic.SpellCheck; using System; using System.Collections.Generic; diff --git a/src/Logic/SpellCheck/Hunspell.cs b/src/Logic/SpellCheck/Hunspell.cs index a3d6e67cc..f04788856 100644 --- a/src/Logic/SpellCheck/Hunspell.cs +++ b/src/Logic/SpellCheck/Hunspell.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Nikse.SubtitleEdit.Core; namespace Nikse.SubtitleEdit.Logic.SpellCheck { diff --git a/src/Logic/UiEbuSaveHelper.cs b/src/Logic/UiEbuSaveHelper.cs new file mode 100644 index 000000000..2becb30d8 --- /dev/null +++ b/src/Logic/UiEbuSaveHelper.cs @@ -0,0 +1,40 @@ +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.SubtitleFormats; +using Nikse.SubtitleEdit.Forms; +using System.Windows.Forms; + +namespace Nikse.SubtitleEdit.Logic +{ + public class UiEbuSaveHelper : Ebu.IEbuUiHelper + { + + private Ebu.EbuGeneralSubtitleInformation _header; + private byte _justificationCode; + private string _fileName; + private Subtitle _subtitle; + + public void Initialize(Ebu.EbuGeneralSubtitleInformation header, byte justificationCode, string fileName, Subtitle subtitle) + { + _header = header; + _justificationCode = justificationCode; + _fileName = fileName; + _subtitle = subtitle; + } + + public bool ShowDialogOk() + { + using (var saveOptions = new EbuSaveOptions()) + { + saveOptions.Initialize(_header, _justificationCode, _fileName, _subtitle); + return saveOptions.ShowDialog() == DialogResult.OK; + } + } + + public byte JustificationCode + { + get { return _justificationCode; } + set { _justificationCode = value; } + } + + } +} diff --git a/src/Logic/UiGetPacEncoding.cs b/src/Logic/UiGetPacEncoding.cs new file mode 100644 index 000000000..4bbcd9df5 --- /dev/null +++ b/src/Logic/UiGetPacEncoding.cs @@ -0,0 +1,24 @@ +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.SubtitleFormats; +using Nikse.SubtitleEdit.Forms; + +namespace Nikse.SubtitleEdit.Logic +{ + public class UiGetPacEncoding : Pac.IGetPacEncoding + { + + public int GetPacEncoding(byte[] previewBuffer, string fileName) + { + using (var pacEncoding = new PacEncoding(previewBuffer, fileName)) + { + if (pacEncoding.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + Configuration.Settings.General.LastPacCodePage = pacEncoding.CodePageIndex; + return pacEncoding.CodePageIndex; + } + return -2; + } + } + + } +} diff --git a/src/Logic/UiGetYouTubeAnnotationStyles.cs b/src/Logic/UiGetYouTubeAnnotationStyles.cs new file mode 100644 index 000000000..29cccfa5e --- /dev/null +++ b/src/Logic/UiGetYouTubeAnnotationStyles.cs @@ -0,0 +1,30 @@ +using Nikse.SubtitleEdit.Core.SubtitleFormats; +using Nikse.SubtitleEdit.Forms; +using System.Collections.Generic; +using System.Windows.Forms; + +namespace Nikse.SubtitleEdit.Logic +{ + public class UiGetYouTubeAnnotationStyles : YouTubeAnnotations.IGetYouTubeAnnotationStyles + { + public List GetYouTubeAnnotationStyles(Dictionary stylesWithCount) + { + if (stylesWithCount == null) + { + return new List(); + } + + using (var import = new YouTubeAnnotationsImport(stylesWithCount)) + { + if (import.ShowDialog() == DialogResult.OK) + { + return import.SelectedStyles; + } + var styles = new List(); + foreach (var k in stylesWithCount.Keys) + styles.Add(k); + return styles; + } + } + } +} diff --git a/src/Logic/UiUtil.cs b/src/Logic/UiUtil.cs new file mode 100644 index 000000000..fedc9baae --- /dev/null +++ b/src/Logic/UiUtil.cs @@ -0,0 +1,248 @@ +using System; +using System.Collections.Generic; +using Nikse.SubtitleEdit.Controls; +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.SubtitleFormats; +using Nikse.SubtitleEdit.Forms; +using Nikse.SubtitleEdit.Logic.VideoPlayers; +using System.IO; + +namespace Nikse.SubtitleEdit.Logic +{ + internal static class UiUtil + { + + public static VideoInfo GetVideoInfo(string fileName) + { + VideoInfo info = Utilities.TryReadVideoInfoViaAviHeader(fileName); + if (info.Success) + return info; + + info = Utilities.TryReadVideoInfoViaMatroskaHeader(fileName); + if (info.Success) + return info; + + info = TryReadVideoInfoViaDirectShow(fileName); + if (info.Success) + return info; + + info = Utilities.TryReadVideoInfoViaMp4(fileName); + if (info.Success) + return info; + + return new VideoInfo { VideoCodec = "Unknown" }; + } + + + private static VideoInfo TryReadVideoInfoViaDirectShow(string fileName) + { + return QuartsPlayer.GetVideoInfo(fileName); + } + + + public static int GetSubtitleIndex(List paragraphs, VideoPlayerContainer videoPlayerContainer) + { + if (videoPlayerContainer.VideoPlayer != null) + { + double positionInMilliseconds = (videoPlayerContainer.VideoPlayer.CurrentPosition * TimeCode.BaseUnit) + 5; + for (int i = 0; i < paragraphs.Count; i++) + { + var p = paragraphs[i]; + if (p.StartTime.TotalMilliseconds <= positionInMilliseconds && p.EndTime.TotalMilliseconds > positionInMilliseconds) + { + bool isInfo = p == paragraphs[0] && (p.StartTime.TotalMilliseconds == 0 && p.Duration.TotalMilliseconds == 0 || p.StartTime.TotalMilliseconds == Pac.PacNullTime.TotalMilliseconds); + if (!isInfo) + return i; + } + } + if (!string.IsNullOrEmpty(videoPlayerContainer.SubtitleText)) + videoPlayerContainer.SetSubtitleText(string.Empty, null); + } + return -1; + } + + public static int ShowSubtitle(List paragraphs, VideoPlayerContainer videoPlayerContainer) + { + if (videoPlayerContainer.VideoPlayer != null) + { + double positionInMilliseconds = (videoPlayerContainer.CurrentPosition * TimeCode.BaseUnit) + 5; + for (int i = 0; i < paragraphs.Count; i++) + { + var p = paragraphs[i]; + if (p.StartTime.TotalMilliseconds <= positionInMilliseconds && + p.EndTime.TotalMilliseconds > positionInMilliseconds) + { + string text = p.Text.Replace("|", Environment.NewLine); + bool isInfo = p == paragraphs[0] && (p.StartTime.TotalMilliseconds == 0 && p.Duration.TotalMilliseconds == 0 || p.StartTime.TotalMilliseconds == Pac.PacNullTime.TotalMilliseconds); + if (!isInfo) + { + if (videoPlayerContainer.LastParagraph != p) + videoPlayerContainer.SetSubtitleText(text, p); + else if (videoPlayerContainer.SubtitleText != text) + videoPlayerContainer.SetSubtitleText(text, p); + return i; + } + } + } + if (!string.IsNullOrEmpty(videoPlayerContainer.SubtitleText)) + videoPlayerContainer.SetSubtitleText(string.Empty, null); + } + return -1; + } + + public static int ShowSubtitle(List paragraphs, Subtitle original, VideoPlayerContainer videoPlayerContainer) + { + if (videoPlayerContainer.VideoPlayer != null) + { + double positionInMilliseconds = (videoPlayerContainer.VideoPlayer.CurrentPosition * TimeCode.BaseUnit) + 15; + for (int i = 0; i < paragraphs.Count; i++) + { + var p = paragraphs[i]; + if (p.StartTime.TotalMilliseconds <= positionInMilliseconds && + p.EndTime.TotalMilliseconds > positionInMilliseconds) + { + var op = Utilities.GetOriginalParagraph(0, p, original.Paragraphs); + + string text = p.Text.Replace("|", Environment.NewLine); + if (op != null) + text = text + Environment.NewLine + Environment.NewLine + op.Text.Replace("|", Environment.NewLine); + + bool isInfo = p == paragraphs[0] && p.StartTime.TotalMilliseconds == 0 && positionInMilliseconds > 3000; + if (!isInfo) + { + if (videoPlayerContainer.LastParagraph != p) + videoPlayerContainer.SetSubtitleText(text, p); + else if (videoPlayerContainer.SubtitleText != text) + videoPlayerContainer.SetSubtitleText(text, p); + return i; + } + } + } + } + if (!string.IsNullOrEmpty(videoPlayerContainer.SubtitleText)) + videoPlayerContainer.SetSubtitleText(string.Empty, null); + return -1; + } + + public static bool IsQuartsDllInstalled + { + get + { + if (Utilities.IsRunningOnMono()) + return false; + + string quartzFileName = Environment.GetFolderPath(Environment.SpecialFolder.System).TrimEnd('\\') + @"\quartz.dll"; + return File.Exists(quartzFileName); + } + } + + public static bool IsManagedDirectXInstalled + { + get + { + if (Utilities.IsRunningOnMono()) + return false; + + try + { + //Check if this folder exists: C:\WINDOWS\Microsoft.NET\DirectX for Managed Code + string folderName = Environment.SystemDirectory.TrimEnd('\\'); + folderName = folderName.Substring(0, folderName.LastIndexOf('\\')); + folderName = folderName + @"\\Microsoft.NET\DirectX for Managed Code"; + return Directory.Exists(folderName); + } + catch (FileNotFoundException) + { + return false; + } + } + } + + public static bool IsMPlayerAvailable + { + get + { + if (Configuration.IsRunningOnLinux() || Utilities.IsRunningOnMono()) + return File.Exists(Path.Combine(Configuration.BaseDirectory, "mplayer")); + + return MPlayer.GetMPlayerFileName != null; + } + } + + public static bool IsMpcHcInstalled + { + get + { + if (Utilities.IsRunningOnMono()) + return false; + + try + { + return VideoPlayers.MpcHC.MpcHc.GetMpcHcFileName() != null; + } + catch (FileNotFoundException) + { + return false; + } + } + } + + public static VideoPlayer GetVideoPlayer() + { + GeneralSettings gs = Configuration.Settings.General; + + if (Configuration.IsRunningOnLinux() || Configuration.IsRunningOnMac()) + return new MPlayer(); + + //if (Utilities.IsRunningOnMac()) + // return new LibVlcMono(); + + if (gs.VideoPlayer == "VLC" && LibVlcDynamic.IsInstalled) + return new LibVlcDynamic(); + + //if (gs.VideoPlayer == "WindowsMediaPlayer" && IsWmpAvailable) + // return new WmpPlayer(); + //if (gs.VideoPlayer == "ManagedDirectX" && IsManagedDirectXInstalled) + // return new ManagedDirectXPlayer(); + + if (gs.VideoPlayer == "MPlayer" && MPlayer.IsInstalled) + return new MPlayer(); + + if (gs.VideoPlayer == "MPC-HC" && VideoPlayers.MpcHC.MpcHc.IsInstalled) + return new VideoPlayers.MpcHC.MpcHc(); + + if (IsQuartsDllInstalled) + return new QuartsPlayer(); + //if (IsWmpAvailable) + // return new WmpPlayer(); + + throw new NotSupportedException("You need DirectX, VLC media player 1.1.x, or MPlayer2 installed as well as Subtitle Edit dll files in order to use the video player!"); + } + + public static void InitializeVideoPlayerAndContainer(string fileName, VideoInfo videoInfo, VideoPlayerContainer videoPlayerContainer, EventHandler onVideoLoaded, EventHandler onVideoEnded) + { + try + { + videoPlayerContainer.VideoPlayer = GetVideoPlayer(); + videoPlayerContainer.VideoPlayer.Initialize(videoPlayerContainer.PanelPlayer, fileName, onVideoLoaded, onVideoEnded); + videoPlayerContainer.ShowStopButton = Configuration.Settings.General.VideoPlayerShowStopButton; + videoPlayerContainer.ShowFullscreenButton = false; + videoPlayerContainer.ShowMuteButton = Configuration.Settings.General.VideoPlayerShowMuteButton; + videoPlayerContainer.Volume = Configuration.Settings.General.VideoPlayerDefaultVolume; + videoPlayerContainer.EnableMouseWheelStep(); + videoPlayerContainer.VideoWidth = videoInfo.Width; + videoPlayerContainer.VideoHeight = videoInfo.Height; + videoPlayerContainer.VideoPlayer.Resize(videoPlayerContainer.PanelPlayer.Width, videoPlayerContainer.PanelPlayer.Height); + } + catch (Exception exception) + { + videoPlayerContainer.VideoPlayer = null; + var videoError = new VideoError(); + videoError.Initialize(fileName, videoInfo, exception); + videoError.ShowDialog(); + } + } + + + } +} diff --git a/src/Logic/VideoPlayers/LibVlcMono.cs b/src/Logic/VideoPlayers/LibVlcMono.cs index 614716a84..8ccd11fec 100644 --- a/src/Logic/VideoPlayers/LibVlcMono.cs +++ b/src/Logic/VideoPlayers/LibVlcMono.cs @@ -1,6 +1,7 @@ using System; using System.Text; using System.Windows.Forms; +using Nikse.SubtitleEdit.Core; namespace Nikse.SubtitleEdit.Logic.VideoPlayers { diff --git a/src/Logic/VideoPlayers/MPlayer.cs b/src/Logic/VideoPlayers/MPlayer.cs index c652b1c70..7f0e49ab5 100644 --- a/src/Logic/VideoPlayers/MPlayer.cs +++ b/src/Logic/VideoPlayers/MPlayer.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Globalization; using System.IO; using System.Windows.Forms; +using Nikse.SubtitleEdit.Core; namespace Nikse.SubtitleEdit.Logic.VideoPlayers { diff --git a/src/Logic/VideoPlayers/QuartsPlayer.cs b/src/Logic/VideoPlayers/QuartsPlayer.cs index 145b7ed1b..2f91013a5 100644 --- a/src/Logic/VideoPlayers/QuartsPlayer.cs +++ b/src/Logic/VideoPlayers/QuartsPlayer.cs @@ -1,6 +1,8 @@ using System; using System.Runtime.InteropServices; using System.Windows.Forms; +using Nikse.Core; +using Nikse.SubtitleEdit.Core; using QuartzTypeLib; using System.ComponentModel; diff --git a/src/SubtitleEdit.csproj b/src/SubtitleEdit.csproj index d24a59931..31e957261 100644 --- a/src/SubtitleEdit.csproj +++ b/src/SubtitleEdit.csproj @@ -88,8 +88,6 @@ AudioVisualizer.cs - - Form @@ -801,102 +799,20 @@ YouTubeAnnotationsImport.cs - - - - - - - - - - - - - - - - - - - - - - - - - + + Code + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Code Code - - @@ -908,297 +824,15 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + @@ -1215,22 +849,7 @@ - - - - - - - - - - - - - - - @@ -1239,7 +858,6 @@ SyncPointsSync.cs - True True @@ -1973,13 +1591,22 @@ SubtitleEdit_SeNetworkService_SeService + + + {3e3cb28f-3a7b-430f-9eb3-0d6c1e53b753} + LibSE + True + + "$(ProjectDir)..\build_helpers.bat" rev && "$(ProjectDir)..\build_helpers.bat" lang COPY /Y /V "$(ProjectDir)packages\NHunspell.1.2.5554.16953\content\*.dll" "$(TargetDir)" -"$(ProjectDir)packages\ILRepack.2.0.1\tools\ILRepack.exe" /out:"$(TargetDir)$(TargetName).exe" "$(TargetDir)$(TargetName).exe" "$(ProjectDir)packages\NHunspell.1.2.5554.16953\lib\net\NHunspell.dll" "$(ProjectDir)packages\zlib.net.1.0.4.0\lib\zlib.net.dll" "$(ProjectDir)DLLs\Interop.QuartzTypeLib.dll" /targetplatform:v4 /internalize /parallel +REM "$(ProjectDir)packages\ILRepack.2.0.1\tools\ILRepack.exe" /out:"$(TargetDir)$(TargetName).exe" "$(TargetDir)$(TargetName).exe" "$(ProjectDir)packages\NHunspell.1.2.5554.16953\lib\net\NHunspell.dll" "$(ProjectDir)packages\zlib.net.1.0.4.0\lib\zlib.net.dll" "$(ProjectDir)DLLs\Interop.QuartzTypeLib.dll" /targetplatform:v4 /internalize /parallel + + diff --git a/src/SubtitleEdit.sln b/src/SubtitleEdit.sln index 40480f9d8..959bde6a4 100644 --- a/src/SubtitleEdit.sln +++ b/src/SubtitleEdit.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 +VisualStudioVersion = 12.0.40629.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SubtitleEdit", "SubtitleEdit.csproj", "{511A5B59-1C35-4719-8536-23B19AF9B21A}" ProjectSection(ProjectDependencies) = postProject @@ -23,6 +23,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UpdateLanguageFiles", "Upda EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{E5F70420-EF49-403E-851F-700953769937}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibSE", "..\libse\LibSE.csproj", "{3E3CB28F-3A7B-430F-9EB3-0D6C1E53B753}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -45,6 +47,10 @@ Global {36BCA2A7-EE6B-45FD-AF90-D3F76A84DA76}.Debug|Any CPU.Build.0 = Debug|Any CPU {36BCA2A7-EE6B-45FD-AF90-D3F76A84DA76}.Release|Any CPU.ActiveCfg = Release|Any CPU {36BCA2A7-EE6B-45FD-AF90-D3F76A84DA76}.Release|Any CPU.Build.0 = Release|Any CPU + {3E3CB28F-3A7B-430F-9EB3-0D6C1E53B753}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3E3CB28F-3A7B-430F-9EB3-0D6C1E53B753}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3E3CB28F-3A7B-430F-9EB3-0D6C1E53B753}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3E3CB28F-3A7B-430F-9EB3-0D6C1E53B753}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Test/FixCommonErrorsTest.cs b/src/Test/FixCommonErrorsTest.cs index 92c5084fb..d4d840eb6 100644 --- a/src/Test/FixCommonErrorsTest.cs +++ b/src/Test/FixCommonErrorsTest.cs @@ -2,10 +2,9 @@ using System.Collections.Generic; using System.IO; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Nikse.SubtitleEdit.Core.Forms; +using Nikse.SubtitleEdit.Core.SubtitleFormats; using Nikse.SubtitleEdit.Forms; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.Forms; -using Nikse.SubtitleEdit.Logic.SubtitleFormats; using Nikse.SubtitleEdit.Core; namespace Test diff --git a/src/Test/Logic/BluRaySup/BluRaySupParserTest.cs b/src/Test/Logic/BluRaySup/BluRaySupParserTest.cs index b5c660a2f..46b1bd7f9 100644 --- a/src/Test/Logic/BluRaySup/BluRaySupParserTest.cs +++ b/src/Test/Logic/BluRaySup/BluRaySupParserTest.cs @@ -2,7 +2,7 @@ using System.Drawing; using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Nikse.SubtitleEdit.Logic.BluRaySup; +using Nikse.SubtitleEdit.Core.BluRaySup; using System.IO; namespace Test.Logic.BluRaySup diff --git a/src/Test/Logic/BluRaySup/ToolBoxTest.cs b/src/Test/Logic/BluRaySup/ToolBoxTest.cs index ce12a530a..cb4f61214 100644 --- a/src/Test/Logic/BluRaySup/ToolBoxTest.cs +++ b/src/Test/Logic/BluRaySup/ToolBoxTest.cs @@ -1,5 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using Nikse.SubtitleEdit.Logic.BluRaySup; +using Nikse.SubtitleEdit.Core.BluRaySup; namespace Test.Logic.BluRaySup { diff --git a/src/Test/Logic/Dictionaries/NamesListTest.cs b/src/Test/Logic/Dictionaries/NamesListTest.cs index 051a8c354..54e4a117c 100644 --- a/src/Test/Logic/Dictionaries/NamesListTest.cs +++ b/src/Test/Logic/Dictionaries/NamesListTest.cs @@ -1,6 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using Nikse.SubtitleEdit.Logic.Dictionaries; using System.IO; +using Nikse.SubtitleEdit.Core.Dictionaries; namespace Test.Logic.Dictionaries { diff --git a/src/Test/Logic/Dictionaries/OcrFixReplaceListTest.cs b/src/Test/Logic/Dictionaries/OcrFixReplaceListTest.cs index d06b53609..e3dde182c 100644 --- a/src/Test/Logic/Dictionaries/OcrFixReplaceListTest.cs +++ b/src/Test/Logic/Dictionaries/OcrFixReplaceListTest.cs @@ -1,7 +1,7 @@ using System; using System.IO; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Nikse.SubtitleEdit.Logic.Dictionaries; +using Nikse.SubtitleEdit.Core.Dictionaries; namespace Test.Logic.Dictionaries { diff --git a/src/Test/Logic/Forms/RemoveTextForHearImpairedTest.cs b/src/Test/Logic/Forms/RemoveTextForHearImpairedTest.cs index 80ed92063..db558153c 100644 --- a/src/Test/Logic/Forms/RemoveTextForHearImpairedTest.cs +++ b/src/Test/Logic/Forms/RemoveTextForHearImpairedTest.cs @@ -1,6 +1,7 @@ using System; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Nikse.SubtitleEdit.Logic.Forms; +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.Forms; namespace Test.Logic.Forms { diff --git a/src/Test/Logic/LanguageTest.cs b/src/Test/Logic/LanguageTest.cs index 385b71ba2..ea85bff0b 100644 --- a/src/Test/Logic/LanguageTest.cs +++ b/src/Test/Logic/LanguageTest.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Xml; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Nikse.SubtitleEdit.Logic; +using Nikse.SubtitleEdit.Core; namespace Test.Logic { diff --git a/src/Test/Logic/Mp4/Mp4Test.cs b/src/Test/Logic/Mp4/Mp4Test.cs index d4ec9e1f3..aa3ce2b21 100644 --- a/src/Test/Logic/Mp4/Mp4Test.cs +++ b/src/Test/Logic/Mp4/Mp4Test.cs @@ -1,6 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using System.IO; -using Nikse.SubtitleEdit.Logic.ContainerFormats.Mp4; +using Nikse.SubtitleEdit.Core.ContainerFormats.Mp4; namespace Test.Logic.Mp4 { diff --git a/src/Test/Logic/Ocr/BinaryOcrTest.cs b/src/Test/Logic/Ocr/BinaryOcrTest.cs index d7fe926bd..c94111460 100644 --- a/src/Test/Logic/Ocr/BinaryOcrTest.cs +++ b/src/Test/Logic/Ocr/BinaryOcrTest.cs @@ -1,7 +1,7 @@ using System.Drawing; using System.IO; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Nikse.SubtitleEdit.Logic; +using Nikse.SubtitleEdit.Core; using Nikse.SubtitleEdit.Logic.Ocr.Binary; namespace Test.Logic.Ocr diff --git a/src/Test/Logic/ParagraphTest.cs b/src/Test/Logic/ParagraphTest.cs index 933eb5b87..04728973c 100644 --- a/src/Test/Logic/ParagraphTest.cs +++ b/src/Test/Logic/ParagraphTest.cs @@ -1,6 +1,6 @@ using System; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Nikse.SubtitleEdit.Logic; +using Nikse.SubtitleEdit.Core; namespace Test.Logic { diff --git a/src/Test/Logic/StripableTextTest.cs b/src/Test/Logic/StripableTextTest.cs index 0a922664a..a5947b55b 100644 --- a/src/Test/Logic/StripableTextTest.cs +++ b/src/Test/Logic/StripableTextTest.cs @@ -1,4 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; +using Nikse.SubtitleEdit.Core; using Nikse.SubtitleEdit.Logic; namespace Test.Logic diff --git a/src/Test/Logic/SubtitleFormats/SubtitleFormatsTest.cs b/src/Test/Logic/SubtitleFormats/SubtitleFormatsTest.cs index 1d96e29c0..817766a46 100644 --- a/src/Test/Logic/SubtitleFormats/SubtitleFormatsTest.cs +++ b/src/Test/Logic/SubtitleFormats/SubtitleFormatsTest.cs @@ -1,12 +1,11 @@ using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; using Nikse.SubtitleEdit.Core; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.SubtitleFormats; using System; using System.Collections.Generic; using System.Globalization; using System.Xml; +using Nikse.SubtitleEdit.Core.SubtitleFormats; namespace Test.Logic.SubtitleFormats { diff --git a/src/Test/Logic/TarFileTest.cs b/src/Test/Logic/TarFileTest.cs index 66649c25f..80c86c0f4 100644 --- a/src/Test/Logic/TarFileTest.cs +++ b/src/Test/Logic/TarFileTest.cs @@ -1,5 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using Nikse.SubtitleEdit.Logic; +using Nikse.SubtitleEdit.Core; using System.IO; namespace Test.Logic diff --git a/src/Test/Logic/TimeCodeTest.cs b/src/Test/Logic/TimeCodeTest.cs index 4d4769f5c..45bf01314 100644 --- a/src/Test/Logic/TimeCodeTest.cs +++ b/src/Test/Logic/TimeCodeTest.cs @@ -1,6 +1,6 @@ using System; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Nikse.SubtitleEdit.Logic; +using Nikse.SubtitleEdit.Core; namespace Test.Logic { diff --git a/src/Test/Logic/TransportStream/TransportStreamTest.cs b/src/Test/Logic/TransportStream/TransportStreamTest.cs index dd60b7505..43822c3c5 100644 --- a/src/Test/Logic/TransportStream/TransportStreamTest.cs +++ b/src/Test/Logic/TransportStream/TransportStreamTest.cs @@ -1,5 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using System.IO; +using Nikse.SubtitleEdit.Core.TransportStream; namespace Test.Logic.TransportStream { @@ -11,7 +12,7 @@ namespace Test.Logic.TransportStream public void TransportStreamTest1() { string fileName = Path.Combine(Directory.GetCurrentDirectory(), "sample_TS_with_graphics.ts"); - var parser = new Nikse.SubtitleEdit.Logic.TransportStream.TransportStreamParser(); + var parser = new TransportStreamParser(); parser.Parse(fileName, null); var subtitles = parser.GetDvbSubtitles(41); diff --git a/src/Test/Logic/UtilitiesTest.cs b/src/Test/Logic/UtilitiesTest.cs index 30084fa9f..f008a9a06 100644 --- a/src/Test/Logic/UtilitiesTest.cs +++ b/src/Test/Logic/UtilitiesTest.cs @@ -1,7 +1,7 @@ using System; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Nikse.SubtitleEdit.Core.Forms; using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.Forms; using Nikse.SubtitleEdit.Core; namespace Test.Logic diff --git a/src/Test/Logic/VideoFormats/MatroskaTest.cs b/src/Test/Logic/VideoFormats/MatroskaTest.cs index 2972e0541..79eac12f6 100644 --- a/src/Test/Logic/VideoFormats/MatroskaTest.cs +++ b/src/Test/Logic/VideoFormats/MatroskaTest.cs @@ -1,7 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.IO; -using Nikse.SubtitleEdit.Logic.ContainerFormats.Matroska; +using Nikse.SubtitleEdit.Core.ContainerFormats.Matroska; namespace Test.Logic.VideoFormats { diff --git a/src/Test/Logic/VobSub/VobSubTest.cs b/src/Test/Logic/VobSub/VobSubTest.cs index 0d28d3d24..633fdc4bc 100644 --- a/src/Test/Logic/VobSub/VobSubTest.cs +++ b/src/Test/Logic/VobSub/VobSubTest.cs @@ -1,6 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using Nikse.SubtitleEdit.Logic; -using Nikse.SubtitleEdit.Logic.VobSub; +using Nikse.SubtitleEdit.Core; +using Nikse.SubtitleEdit.Core.VobSub; using System; using System.Drawing; diff --git a/src/Test/Test.csproj b/src/Test/Test.csproj index 6ed8fe3b6..faefd0643 100644 --- a/src/Test/Test.csproj +++ b/src/Test/Test.csproj @@ -68,6 +68,10 @@ + + {3e3cb28f-3a7b-430f-9eb3-0d6c1e53b753} + LibSE + {511A5B59-1C35-4719-8536-23B19AF9B21A} SubtitleEdit