From 0758e4931d64a1f8f35786972c4ed0bd9997ba76 Mon Sep 17 00:00:00 2001 From: Vseslav Kochenov Date: Fri, 2 Jun 2023 16:40:42 +0300 Subject: [PATCH 1/3] Notes: add .ass format support and drag&drop support --- SnM/SnM.h | 3 +- SnM/SnM_Notes.cpp | 304 +++++++++++++++++++++++++++++++++++++++++----- SnM/SnM_Notes.h | 3 +- sws_extension.rc | 1 + 4 files changed, 280 insertions(+), 31 deletions(-) diff --git a/SnM/SnM.h b/SnM/SnM.h index 7817dcfdb..c3bc69a39 100644 --- a/SnM/SnM.h +++ b/SnM/SnM.h @@ -125,7 +125,8 @@ #define SNM_CSURF_EXT_UNREGISTER 0x00016666 #define SNM_REAPER_IMG_EXTS "png,pcx,jpg,jpeg,jfif,ico,bmp" // img exts supported by REAPER (v4.32), can't get those at runtime yet #define SNM_INI_EXT_LIST "INI files (*.INI)\0*.INI\0All Files\0*.*\0" -#define SNM_SUB_EXT_LIST "SubRip subtitle files (*.SRT)\0*.SRT\0" +#define SNM_SUB_IMPORT_EXT_LIST "Subtitle files (*.SRT, *.ASS)\0*.SRT;*.ASS\0" +#define SNM_SUB_EXT_LIST "Subtitle files (*.SRT)\0*.SRT\0" #define SNM_TXT_EXT_LIST "Text files (*.txt)\0*.txt\0All files (*.*)\0*.*\0" #define SNM_MARKER_MASK 1 diff --git a/SnM/SnM_Notes.cpp b/SnM/SnM_Notes.cpp index 73c9362ef..59dd3d65b 100644 --- a/SnM/SnM_Notes.cpp +++ b/SnM/SnM_Notes.cpp @@ -479,7 +479,7 @@ void NotesWnd::OnTimer(WPARAM wParam) // => updates during playback, in other cases (e.g. item selection change) the main // window will be the active one, not our NotesWnd if (g_notesType!=SNM_NOTES_PROJECT && g_notesType!=SNM_NOTES_PROJECT_EXTRA && g_notesType!=SNM_NOTES_GLOBAL && - IsWindowVisible(m_hwnd) && (!IsActive() || (g_locked && (g_notesType>=SNM_NOTES_MKR_NAME && g_notesType<=SNM_NOTES_MKRRGN_SUB)))) + IsWindowVisible(m_hwnd) && (!IsActive() || (g_locked && (g_notesType>=SNM_NOTES_MKR_NAME && g_notesType<=SNM_NOTES_MKRRGN_SUB)))) { Update(); } @@ -493,9 +493,9 @@ void NotesWnd::OnResize() // room for buttons? if ( #ifdef WANT_ACTION_HELP - g_notesType==SNM_NOTES_ACTION_HELP || + g_notesType==SNM_NOTES_ACTION_HELP || #endif - (g_notesType>=SNM_NOTES_MKR_SUB && g_notesType<=SNM_NOTES_MKRRGN_SUB)) { + (g_notesType>=SNM_NOTES_MKR_SUB && g_notesType<=SNM_NOTES_MKRRGN_SUB)) { m_resize.get_item(IDC_EDIT1)->orig.bottom = m_resize.get_item(IDC_EDIT1)->real_orig.bottom - 41; //JFB!! 41 is tied to the current .rc! m_resize.get_item(IDC_EDIT2)->orig.bottom = m_resize.get_item(IDC_EDIT2)->real_orig.bottom - 41; //JFB!! 41 is tied to the current .rc! } @@ -689,9 +689,9 @@ void NotesWnd::ToggleLock() /////////////////////////////////////////////////////////////////////////////// -void NotesWnd::SaveCurrentText(int _type, bool _wantUndo) +void NotesWnd::SaveCurrentText(int _type, bool _wantUndo) { - switch(_type) + switch(_type) { case SNM_NOTES_PROJECT: SaveCurrentProjectNotes(_wantUndo); @@ -699,8 +699,8 @@ void NotesWnd::SaveCurrentText(int _type, bool _wantUndo) case SNM_NOTES_PROJECT_EXTRA: SaveCurrentExtraProjectNotes(_wantUndo); break; - case SNM_NOTES_ITEM: - SaveCurrentItemNotes(_wantUndo); + case SNM_NOTES_ITEM: + SaveCurrentItemNotes(_wantUndo); break; case SNM_NOTES_TRACK: SaveCurrentTrackNotes(_wantUndo); @@ -755,7 +755,7 @@ void NotesWnd::SaveCurrentItemNotes(bool _wantUndo) if (GetSetMediaItemInfo(g_mediaItemNote, "P_NOTES", g_lastText)) { // UpdateItemInProject(g_mediaItemNote); - UpdateTimeline(); // for the item's note button + UpdateTimeline(); // for the item's note button if (_wantUndo) Undo_OnStateChangeEx2(NULL, __LOCALIZE("Edit item notes","sws_undo"), UNDO_STATE_ALL, -1); //JFB TODO? -1 to replace? UNDO_STATE_ITEMS? else @@ -770,7 +770,7 @@ void NotesWnd::SaveCurrentTrackNotes(bool _wantUndo) { GetWindowText(m_edit, g_lastText, sizeof(g_lastText)); bool found = false; - for (int i=0; i < g_SNM_TrackNotes.Get()->GetSize(); i++) + for (int i=0; i < g_SNM_TrackNotes.Get()->GetSize(); i++) { if (g_SNM_TrackNotes.Get()->Get(i)->GetTrack() == g_trNote) { @@ -836,7 +836,7 @@ void NotesWnd::SaveCurrentMkrRgnNameOrSub(int _type, bool _wantUndo) { // CRLF removed only when saving the project.. bool found = false; - for (int i=0; i < g_pRegionSubs.Get()->GetSize(); i++) + for (int i=0; i < g_pRegionSubs.Get()->GetSize(); i++) { if (g_pRegionSubs.Get()->Get(i)->GetId() == g_lastMarkerRegionId) { g_pRegionSubs.Get()->Get(i)->SetNotes(g_lastText); @@ -932,7 +932,7 @@ void NotesWnd::Update(bool _force) break; #endif } - + if (_force || refreshType == REQUEST_REFRESH) RefreshGUI(); @@ -944,8 +944,8 @@ int NotesWnd::UpdateActionHelp() { int refreshType = NO_REFRESH; int iSel = GetSelectedAction( - g_lastActionSection, SNM_MAX_SECTION_NAME_LEN, &g_lastActionListCmd, - g_lastActionCustId, SNM_MAX_ACTION_CUSTID_LEN, + g_lastActionSection, SNM_MAX_SECTION_NAME_LEN, &g_lastActionListCmd, + g_lastActionCustId, SNM_MAX_ACTION_CUSTID_LEN, g_lastActionDesc, SNM_MAX_ACTION_NAME_LEN); if (iSel >= 0) @@ -987,7 +987,7 @@ int NotesWnd::UpdateItemNotes() if (char* notes = (char*)GetSetMediaItemInfo(g_mediaItemNote, "P_NOTES", NULL)) SetText(notes, false); refreshType = REQUEST_REFRESH; - } + } } else if (g_mediaItemNote || *g_lastText) { @@ -1016,7 +1016,7 @@ int NotesWnd::UpdateTrackNotes() g_SNM_TrackNotes.Get()->Add(new SNM_TrackNotes(nullptr, TrackToGuid(g_trNote), "")); SetText(""); refreshType = REQUEST_REFRESH; - } + } } else if (g_trNote || *g_lastText) { @@ -1207,13 +1207,13 @@ bool GetStringFromNotesChunk(WDL_FastString* _notesIn, char* _bufOut, int _bufOu int i=0; while (pNotes[i] && pNotes[i] != '|') i++; if (pNotes[i]) i++; else return true; - + int j=0; while (pNotes[i] && j < _bufOutSz) { if (pNotes[i] != '\r' && pNotes[i] != '\n') { - _bufOut[j++] = (pNotes[i]=='|'&&pNotes[i-1]=='\n' ? '\n' : pNotes[i]); // i is >0 here + _bufOut[j++] = (pNotes[i]=='|'&&pNotes[i-1]=='\n' ? '\n' : pNotes[i]); // i is >0 here } i++; } @@ -1233,9 +1233,9 @@ bool GetNotesChunkFromString(const char* _bufIn, WDL_FastString* _notesOut, cons int i=0; while (_bufIn[i]) { - if (_bufIn[i] == '\n') + if (_bufIn[i] == '\n') _notesOut->Append("\n|"); - else if (_bufIn[i] != '\r') + else if (_bufIn[i] != '\r') _notesOut->Append(_bufIn+i, 1); i++; } @@ -1279,7 +1279,7 @@ void NotesMarkerRegionListener::NotifyMarkerRegionUpdate(int _updateFlags) /////////////////////////////////////////////////////////////////////////////// -// import/export subtitle files, only SubRip (.srt) files atm +// import/export subtitle files, only SubRip (.srt) and Advanced SubStation Alpha (.ass) files atm // see http://en.wikipedia.org/wiki/SubRip#Specifications bool ImportSubRipFile(const char* _fn) @@ -1290,6 +1290,14 @@ bool ImportSubRipFile(const char* _fn) // no need to check extension here, it's done for us if (FILE* f = fopenUTF8(_fn, "rt")) { + char bom[4]; + if (!fgets(bom, 4, f) || !*bom) { + fclose(f); + return ok; + } + if (!(bom[0] == char(0xEF) && bom[1] == char(0xBB) && bom[2] == char(0xBF))) { + rewind(f); + } char buf[1024]; while(fgets(buf, sizeof(buf), f) && *buf) { @@ -1310,7 +1318,7 @@ bool ImportSubRipFile(const char* _fn) if (*buf == '\r' || *buf == '\n') break; notes.Append(buf); } - + WDL_String name(notes.Get()); char *p=name.Get(); while (*p) { @@ -1352,17 +1360,221 @@ bool ImportSubRipFile(const char* _fn) return ok; } +// see http://moodub.free.fr/video/ass-specs.doc +bool ImportAdvancedSubStationFile(const char* _fn) +{ + bool ok = false; + double firstPos = -1.0; + + // no need to check extension here, it's done for us + if (FILE* f = fopenUTF8(_fn, "rt")) + { + char bom[4]; + if (!fgets(bom, 4, f) || !*bom) { + fclose(f); + return ok; + } + if (!(bom[0] == char(0xEF) && bom[1] == char(0xBB) && bom[2] == char(0xBF))) { + rewind(f); + } + char buf[1024]; + while(fgets(buf, sizeof(buf), f) && *buf) // searching for Events block + { + if (strncmp(buf, "[Events]", 8) == 0) // searching for events block, we don't care about styles and stuff + { + break; + } + } + if (!(fgets(buf, sizeof(buf), f) && *buf)) + return false; // reading and discarding format line, we're only interested in fields that are fixed + + while(fgets(buf, sizeof(buf), f) && *buf) // reading events + { + char *commaPos = strchr(buf, ',') ; + if (commaPos == nullptr) { + break; + } + commaPos++; + int commaCount = 1; + + int p1[4], p2[4]; + if (sscanf(commaPos, "%d:%d:%d%*c%d,%d:%d:%d%*c%d", // parsing start and end times + &p1[0], &p1[1], &p1[2], &p1[3], + &p2[0], &p2[1], &p2[2], &p2[3]) != 8) + break; + for (;commaCount < 4; commaCount++) { + commaPos = strchr(commaPos+1, ','); + + while (!commaPos && !strchr(buf, '\n') && fgets(buf, sizeof(buf), f) && *buf) { + commaPos = strchr(buf, ','); // in case Style is insanely long, read more of the line but break if found new line + } + if (!commaPos) { // if newline found before enough commas, file is malformed + break; + } + } + if (commaCount != 4) { + break; + } + commaPos++; + WDL_FastString notes; + if (*commaPos != ',') { // if Name field isn't empty + notes.Append("["); + char* nextCommaPos = strchr(commaPos, ','); + while (!nextCommaPos && !strchr(buf, '\n') && fgets(buf, sizeof(buf), f) && *buf) { // in case comma + notes.Append(commaPos); + commaPos = buf; + nextCommaPos = strchr(buf, ','); + } + char nameBuf[1024]; + memcpy(nameBuf, commaPos, nextCommaPos-commaPos); + nameBuf[nextCommaPos-commaPos] = '\0'; + notes.Append(nameBuf); + notes.Append("] "); + commaPos = nextCommaPos; + } + commaCount++; + while (commaCount < 9 && !(*commaPos == '\n' || *commaPos == '\r')) { + commaPos = strchr(commaPos+1, ','); + while (commaPos == nullptr) { + if (!(fgets(buf, sizeof(buf), f) && *buf && !(*buf == '\n' || *buf == '\r'))) { + break; + } + commaPos = strchr(buf, ','); + } + if (*buf == '\n' || *buf == '\r') { + break; + } + commaCount++; + } + if (commaCount != 9) { + break; + } + char *textPos = commaPos + 1; + + char text[1024]; + int j = 0; + + enum parseState {Normal, Escaped, InBrackets}; + parseState ps = Normal; + while (*textPos != '\r' && *textPos != '\n') // dumb but fast way to filter out formatting blocks and properly format line break + { + char current = *textPos; + switch (ps) + { + case Escaped: + switch (current) + { + case 'n': + case 'N': + text[j] = '\n'; + j++; + break; + case 'h': + text[j] = ' '; + j++; + break; + default: + text[j] = '\\'; + text[j+1] = current; + j += 2; + } + ps = Normal; + break; + case Normal: + switch (current) + { + case '\\': + ps = Escaped; + break; + case '{': + ps = InBrackets; + break; + default: + text[j]=current; + j++; + } + break; + case InBrackets: + if (current == '}') + ps = Normal; + break; + } + if (j == 1023) { + text[j] = '\0'; + notes.Append(text); + j=0; + } + textPos += 1; + if (!*textPos) { + if (!fgets(buf, sizeof(buf), f) || !*buf) { + break; + } + textPos = buf; + } + } + text[j] = '\0'; + notes.Append(text); // from here mostly the same as SRT + + WDL_String name(notes.Get()); + char *p=name.Get(); + while (*p) { + if (*p == '\r' || *p == '\n') *p=' '; + p++; + } + name.Ellipsize(0, 64); // 64 = native max mkr/rgn name length + int num = AddProjectMarker(NULL, true, + float(p1[0])*3600 + float(p1[1])*60 + float(p1[2]) + float(p1[3])/100, + float(p2[0])*3600 + float(p2[1])*60 + float(p2[2]) + float(p2[3])/100, + name.Get(), -1); + if (num >= 0) + { + ok = true; // region added (at least) + + if (firstPos < 0.0) + firstPos = float(p1[0])*3600 + float(p1[1])*60 + p1[2] + p1[3]/100; + + int id = MakeMarkerRegionId(num, true); + if (id > 0) // add the sub, no duplicate mgmt.. + g_pRegionSubs.Get()->Add(new SNM_RegionSubtitle(nullptr, id, notes.Get())); + } + + } + + fclose(f); + } + + if (ok) + { + UpdateTimeline(); // redraw the ruler (andd arrange view) + if (firstPos > 0.0) + SetEditCurPos2(NULL, firstPos, true, false); + } + return ok; +} + void ImportSubTitleFile(COMMAND_T* _ct) { - if (char* fn = BrowseForFiles(__LOCALIZE("S&M - Import subtitle file","sws_DLG_152"), g_lastImportSubFn, NULL, false, SNM_SUB_EXT_LIST)) + if (char* fn = BrowseForFiles(__LOCALIZE("S&M - Import subtitle file","sws_DLG_152"), g_lastImportSubFn, NULL, false, SNM_SUB_IMPORT_EXT_LIST)) { lstrcpyn(g_lastImportSubFn, fn, sizeof(g_lastImportSubFn)); - if (ImportSubRipFile(fn)) - //JFB hard-coded undo label: _ct might be NULL (when called from a button) - // + avoid trailing "..." in undo point name (when called from an action) - Undo_OnStateChangeEx2(NULL, __LOCALIZE("Import subtitle file","sws_DLG_152"), UNDO_STATE_ALL, -1); - else - MessageBox(GetMainHwnd(), __LOCALIZE("Invalid subtitle file!","sws_DLG_152"), __LOCALIZE("S&M - Error","sws_DLG_152"), MB_OK); + + if (HasFileExtension(fn, "srt")) { + if (ImportSubRipFile(fn)) + //JFB hard-coded undo label: _ct might be NULL (when called from a button) + // + avoid trailing "..." in undo point name (when called from an action) + Undo_OnStateChangeEx2(NULL, __LOCALIZE("Import subtitle file","sws_DLG_152"), UNDO_STATE_ALL, -1); + else + MessageBox(GetMainHwnd(), __LOCALIZE("Invalid subtitle file!","sws_DLG_152"), __LOCALIZE("S&M - Error","sws_DLG_152"), MB_OK); + } else if (HasFileExtension(fn, "ass")) { + if (ImportAdvancedSubStationFile(fn)) + //JFB hard-coded undo label: _ct might be NULL (when called from a button) + // + avoid trailing "..." in undo point name (when called from an action) + Undo_OnStateChangeEx2(NULL, __LOCALIZE("Import ASS file","sws_DLG_152"), UNDO_STATE_ALL, -1); + else + MessageBox(GetMainHwnd(), __LOCALIZE("Invalid ASS file!","sws_DLG_152"), __LOCALIZE("S&M - Error","sws_DLG_152"), MB_OK); + } else { + MessageBox(GetMainHwnd(), __LOCALIZE("Invalid or unsupported file format!","sws_DLG_152"), __LOCALIZE("S&M - Error","sws_DLG_152"), MB_OK); + } free(fn); } } @@ -1686,7 +1898,7 @@ int IsNotesLocked(COMMAND_T*) { } /****************************************************************************** -* ReaScript export #755 * +* ReaScript export #755 * ******************************************************************************/ const char* NFDoGetSWSTrackNotes(MediaTrack* track) { @@ -1804,3 +2016,37 @@ void NF_DoUpdateSWSMarkerRegionSubWindow() w->ForceUpdateMkrRgnNameOrSub(g_notesType); } } + +void NotesWnd::OnDroppedFiles(HDROP _h) { + if (g_locked || !(g_notesType == SNM_NOTES_MKR_SUB || g_notesType == SNM_NOTES_RGN_SUB || g_notesType == SNM_NOTES_MKRRGN_SUB)) { + return; + } + int iFiles = DragQueryFile(_h, 0xFFFFFFFF, NULL, 0); + char fn[SNM_MAX_PATH] = ""; + for (int i = 0; i < iFiles; i++) { + DragQueryFile(_h, i, fn, sizeof(fn)); + + if (HasFileExtension(fn, "srt")) { + if (ImportSubRipFile(fn)) + //JFB hard-coded undo label: _ct might be NULL (when called from a button) + // + avoid trailing "..." in undo point name (when called from an action) + Undo_OnStateChangeEx2(NULL, __LOCALIZE("Import subtitle file", "sws_DLG_152"), UNDO_STATE_ALL, + -1); + else + MessageBox(GetMainHwnd(), __LOCALIZE("Invalid subtitle file!", "sws_DLG_152"), + __LOCALIZE("S&M - Error", "sws_DLG_152"), MB_OK); + } else if (HasFileExtension(fn, "ass")) { + if (ImportAdvancedSubStationFile(fn)) + //JFB hard-coded undo label: _ct might be NULL (when called from a button) + // + avoid trailing "..." in undo point name (when called from an action) + Undo_OnStateChangeEx2(NULL, __LOCALIZE("Import ass file", "sws_DLG_152"), UNDO_STATE_ALL, -1); + else + MessageBox(GetMainHwnd(), __LOCALIZE("Invalid ass file!", "sws_DLG_152"), + __LOCALIZE("S&M - Error", "sws_DLG_152"), MB_OK); + } else { + MessageBox(GetMainHwnd(), __LOCALIZE("Invalid or unsupported file format!", "sws_DLG_152"), + __LOCALIZE("S&M - Error", "sws_DLG_152"), MB_OK); + } + } + +} diff --git a/SnM/SnM_Notes.h b/SnM/SnM_Notes.h index b5242ce70..72f74a684 100644 --- a/SnM/SnM_Notes.h +++ b/SnM/SnM_Notes.h @@ -162,6 +162,7 @@ class NotesWnd : public SWS_DockWnd HMENU OnContextMenu(int x, int y, bool* wantDefaultItems); int OnKey(MSG* msg, int iKeyState); void OnTimer(WPARAM wParam=0); + void OnDroppedFiles(HDROP h); void OnResize(); void DrawControls(LICE_IBitmap* _bm, const RECT* _r, int* _tooltipHeight = NULL); bool GetToolTipString(int _xpos, int _ypos, char* _bufOut, int _bufOutSz); @@ -172,7 +173,7 @@ class NotesWnd : public SWS_DockWnd #ifdef WANT_ACTION_HELP SNM_ToolbarButton m_btnAlr, m_btnActionList; #endif - SNM_ToolbarButton m_btnImportSub, m_btnExportSub; + SNM_ToolbarButton m_btnImportSub, m_btnImportAss, m_btnExportSub; WDL_VirtualStaticText m_txtLabel; SNM_DynSizedText m_bigNotes; diff --git a/sws_extension.rc b/sws_extension.rc index 871a96070..5cdf0629a 100644 --- a/sws_extension.rc +++ b/sws_extension.rc @@ -850,6 +850,7 @@ END IDD_SNM_NOTES DIALOGEX 0, 0, 281, 125 STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +EXSTYLE WS_EX_ACCEPTFILES CAPTION "S&M - Notes" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN From 9700013e05f5520fd53407ee1e4f835154ab8fe7 Mon Sep 17 00:00:00 2001 From: cfillion Date: Sun, 4 Feb 2024 06:11:09 -0500 Subject: [PATCH 2/3] de-duplicate import/drop code, abort on first drop failure, remove unused btnImportAss member and cleanup whitespace --- SnM/SnM_Notes.cpp | 134 ++++++++++++++++++++-------------------------- SnM/SnM_Notes.h | 4 +- 2 files changed, 60 insertions(+), 78 deletions(-) diff --git a/SnM/SnM_Notes.cpp b/SnM/SnM_Notes.cpp index 59dd3d65b..1f99e5a8d 100644 --- a/SnM/SnM_Notes.cpp +++ b/SnM/SnM_Notes.cpp @@ -493,7 +493,7 @@ void NotesWnd::OnResize() // room for buttons? if ( #ifdef WANT_ACTION_HELP - g_notesType==SNM_NOTES_ACTION_HELP || + g_notesType==SNM_NOTES_ACTION_HELP || #endif (g_notesType>=SNM_NOTES_MKR_SUB && g_notesType<=SNM_NOTES_MKRRGN_SUB)) { m_resize.get_item(IDC_EDIT1)->orig.bottom = m_resize.get_item(IDC_EDIT1)->real_orig.bottom - 41; //JFB!! 41 is tied to the current .rc! @@ -1212,9 +1212,7 @@ bool GetStringFromNotesChunk(WDL_FastString* _notesIn, char* _bufOut, int _bufOu while (pNotes[i] && j < _bufOutSz) { if (pNotes[i] != '\r' && pNotes[i] != '\n') - { - _bufOut[j++] = (pNotes[i]=='|'&&pNotes[i-1]=='\n' ? '\n' : pNotes[i]); // i is >0 here - } + _bufOut[j++] = (pNotes[i]=='|'&&pNotes[i-1]=='\n' ? '\n' : pNotes[i]); // i is >0 here i++; } if (j>=1 && !strcmp(_bufOut+j-1, ">")) // remove trailing ">", if any @@ -1318,7 +1316,7 @@ bool ImportSubRipFile(const char* _fn) if (*buf == '\r' || *buf == '\n') break; notes.Append(buf); } - + WDL_String name(notes.Get()); char *p=name.Get(); while (*p) { @@ -1381,9 +1379,7 @@ bool ImportAdvancedSubStationFile(const char* _fn) while(fgets(buf, sizeof(buf), f) && *buf) // searching for Events block { if (strncmp(buf, "[Events]", 8) == 0) // searching for events block, we don't care about styles and stuff - { break; - } } if (!(fgets(buf, sizeof(buf), f) && *buf)) return false; // reading and discarding format line, we're only interested in fields that are fixed @@ -1394,8 +1390,8 @@ bool ImportAdvancedSubStationFile(const char* _fn) if (commaPos == nullptr) { break; } - commaPos++; - int commaCount = 1; + commaPos++; + int commaCount = 1; int p1[4], p2[4]; if (sscanf(commaPos, "%d:%d:%d%*c%d,%d:%d:%d%*c%d", // parsing start and end times @@ -1429,27 +1425,27 @@ bool ImportAdvancedSubStationFile(const char* _fn) memcpy(nameBuf, commaPos, nextCommaPos-commaPos); nameBuf[nextCommaPos-commaPos] = '\0'; notes.Append(nameBuf); - notes.Append("] "); + notes.Append("] "); commaPos = nextCommaPos; } - commaCount++; + commaCount++; while (commaCount < 9 && !(*commaPos == '\n' || *commaPos == '\r')) { - commaPos = strchr(commaPos+1, ','); - while (commaPos == nullptr) { - if (!(fgets(buf, sizeof(buf), f) && *buf && !(*buf == '\n' || *buf == '\r'))) { - break; - } - commaPos = strchr(buf, ','); - } - if (*buf == '\n' || *buf == '\r') { - break; - } - commaCount++; + commaPos = strchr(commaPos+1, ','); + while (commaPos == nullptr) { + if (!(fgets(buf, sizeof(buf), f) && *buf && !(*buf == '\n' || *buf == '\r'))) { + break; + } + commaPos = strchr(buf, ','); + } + if (*buf == '\n' || *buf == '\r') { + break; + } + commaCount++; } if (commaCount != 9) { break; } - char *textPos = commaPos + 1; + char *textPos = commaPos + 1; char text[1024]; int j = 0; @@ -1509,7 +1505,7 @@ bool ImportAdvancedSubStationFile(const char* _fn) if (!fgets(buf, sizeof(buf), f) || !*buf) { break; } - textPos = buf; + textPos = buf; } } text[j] = '\0'; @@ -1523,9 +1519,9 @@ bool ImportAdvancedSubStationFile(const char* _fn) } name.Ellipsize(0, 64); // 64 = native max mkr/rgn name length int num = AddProjectMarker(NULL, true, - float(p1[0])*3600 + float(p1[1])*60 + float(p1[2]) + float(p1[3])/100, - float(p2[0])*3600 + float(p2[1])*60 + float(p2[2]) + float(p2[3])/100, - name.Get(), -1); + float(p1[0])*3600 + float(p1[1])*60 + float(p1[2]) + float(p1[3])/100, + float(p2[0])*3600 + float(p2[1])*60 + float(p2[2]) + float(p2[3])/100, + name.Get(), -1); if (num >= 0) { ok = true; // region added (at least) @@ -1537,7 +1533,6 @@ bool ImportAdvancedSubStationFile(const char* _fn) if (id > 0) // add the sub, no duplicate mgmt.. g_pRegionSubs.Get()->Add(new SNM_RegionSubtitle(nullptr, id, notes.Get())); } - } fclose(f); @@ -1552,29 +1547,38 @@ bool ImportAdvancedSubStationFile(const char* _fn) return ok; } +static bool ImportSubTitleFile(const char *fn) +{ + constexpr struct { const char *ext; bool (*reader)(const char *); } formats[] { + { "srt", &ImportSubRipFile }, + { "ass", &ImportAdvancedSubStationFile }, + }; + + for (const auto &format : formats) + { + if (!HasFileExtension(fn, format.ext)) + continue; + + if (format.reader(fn)) { + Undo_OnStateChangeEx2(NULL, __LOCALIZE("Import subtitle file","sws_DLG_152"), UNDO_STATE_ALL, -1); + return true; + } + else { + MessageBox(GetMainHwnd(), __LOCALIZE("Invalid subtitle file!","sws_DLG_152"), __LOCALIZE("S&M - Error","sws_DLG_152"), MB_OK); + return false; + } + } + + MessageBox(GetMainHwnd(), __LOCALIZE("Invalid or unsupported file format!","sws_DLG_152"), __LOCALIZE("S&M - Error","sws_DLG_152"), MB_OK); + return false; +} + void ImportSubTitleFile(COMMAND_T* _ct) { if (char* fn = BrowseForFiles(__LOCALIZE("S&M - Import subtitle file","sws_DLG_152"), g_lastImportSubFn, NULL, false, SNM_SUB_IMPORT_EXT_LIST)) { lstrcpyn(g_lastImportSubFn, fn, sizeof(g_lastImportSubFn)); - - if (HasFileExtension(fn, "srt")) { - if (ImportSubRipFile(fn)) - //JFB hard-coded undo label: _ct might be NULL (when called from a button) - // + avoid trailing "..." in undo point name (when called from an action) - Undo_OnStateChangeEx2(NULL, __LOCALIZE("Import subtitle file","sws_DLG_152"), UNDO_STATE_ALL, -1); - else - MessageBox(GetMainHwnd(), __LOCALIZE("Invalid subtitle file!","sws_DLG_152"), __LOCALIZE("S&M - Error","sws_DLG_152"), MB_OK); - } else if (HasFileExtension(fn, "ass")) { - if (ImportAdvancedSubStationFile(fn)) - //JFB hard-coded undo label: _ct might be NULL (when called from a button) - // + avoid trailing "..." in undo point name (when called from an action) - Undo_OnStateChangeEx2(NULL, __LOCALIZE("Import ASS file","sws_DLG_152"), UNDO_STATE_ALL, -1); - else - MessageBox(GetMainHwnd(), __LOCALIZE("Invalid ASS file!","sws_DLG_152"), __LOCALIZE("S&M - Error","sws_DLG_152"), MB_OK); - } else { - MessageBox(GetMainHwnd(), __LOCALIZE("Invalid or unsupported file format!","sws_DLG_152"), __LOCALIZE("S&M - Error","sws_DLG_152"), MB_OK); - } + ImportSubTitleFile(fn); free(fn); } } @@ -1898,7 +1902,7 @@ int IsNotesLocked(COMMAND_T*) { } /****************************************************************************** -* ReaScript export #755 * +* ReaScript export #755 * ******************************************************************************/ const char* NFDoGetSWSTrackNotes(MediaTrack* track) { @@ -2018,35 +2022,13 @@ void NF_DoUpdateSWSMarkerRegionSubWindow() } void NotesWnd::OnDroppedFiles(HDROP _h) { - if (g_locked || !(g_notesType == SNM_NOTES_MKR_SUB || g_notesType == SNM_NOTES_RGN_SUB || g_notesType == SNM_NOTES_MKRRGN_SUB)) { + if (g_locked || !(g_notesType == SNM_NOTES_MKR_SUB || g_notesType == SNM_NOTES_RGN_SUB || g_notesType == SNM_NOTES_MKRRGN_SUB)) return; + const int iFiles = DragQueryFile(_h, 0xFFFFFFFF, NULL, 0); + char fn[SNM_MAX_PATH]; + for (int i = 0; i < iFiles; i++) + { + if (DragQueryFile(_h, i, fn, sizeof(fn)) && !ImportSubTitleFile(fn)) + break; } - int iFiles = DragQueryFile(_h, 0xFFFFFFFF, NULL, 0); - char fn[SNM_MAX_PATH] = ""; - for (int i = 0; i < iFiles; i++) { - DragQueryFile(_h, i, fn, sizeof(fn)); - - if (HasFileExtension(fn, "srt")) { - if (ImportSubRipFile(fn)) - //JFB hard-coded undo label: _ct might be NULL (when called from a button) - // + avoid trailing "..." in undo point name (when called from an action) - Undo_OnStateChangeEx2(NULL, __LOCALIZE("Import subtitle file", "sws_DLG_152"), UNDO_STATE_ALL, - -1); - else - MessageBox(GetMainHwnd(), __LOCALIZE("Invalid subtitle file!", "sws_DLG_152"), - __LOCALIZE("S&M - Error", "sws_DLG_152"), MB_OK); - } else if (HasFileExtension(fn, "ass")) { - if (ImportAdvancedSubStationFile(fn)) - //JFB hard-coded undo label: _ct might be NULL (when called from a button) - // + avoid trailing "..." in undo point name (when called from an action) - Undo_OnStateChangeEx2(NULL, __LOCALIZE("Import ass file", "sws_DLG_152"), UNDO_STATE_ALL, -1); - else - MessageBox(GetMainHwnd(), __LOCALIZE("Invalid ass file!", "sws_DLG_152"), - __LOCALIZE("S&M - Error", "sws_DLG_152"), MB_OK); - } else { - MessageBox(GetMainHwnd(), __LOCALIZE("Invalid or unsupported file format!", "sws_DLG_152"), - __LOCALIZE("S&M - Error", "sws_DLG_152"), MB_OK); - } - } - } diff --git a/SnM/SnM_Notes.h b/SnM/SnM_Notes.h index 72f74a684..70d65f064 100644 --- a/SnM/SnM_Notes.h +++ b/SnM/SnM_Notes.h @@ -162,7 +162,7 @@ class NotesWnd : public SWS_DockWnd HMENU OnContextMenu(int x, int y, bool* wantDefaultItems); int OnKey(MSG* msg, int iKeyState); void OnTimer(WPARAM wParam=0); - void OnDroppedFiles(HDROP h); + void OnDroppedFiles(HDROP h); void OnResize(); void DrawControls(LICE_IBitmap* _bm, const RECT* _r, int* _tooltipHeight = NULL); bool GetToolTipString(int _xpos, int _ypos, char* _bufOut, int _bufOutSz); @@ -173,7 +173,7 @@ class NotesWnd : public SWS_DockWnd #ifdef WANT_ACTION_HELP SNM_ToolbarButton m_btnAlr, m_btnActionList; #endif - SNM_ToolbarButton m_btnImportSub, m_btnImportAss, m_btnExportSub; + SNM_ToolbarButton m_btnImportSub, m_btnExportSub; WDL_VirtualStaticText m_txtLabel; SNM_DynSizedText m_bigNotes; From 0ced4fe1330cf1640ed8eada3f8bd875f387e952 Mon Sep 17 00:00:00 2001 From: cfillion Date: Sun, 4 Feb 2024 07:26:09 -0500 Subject: [PATCH 3/3] fix three cases of truncated lines causing cashes and don't drop the first 1K chars of the name field when larger than one buffer --- SnM/SnM_Notes.cpp | 46 +++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/SnM/SnM_Notes.cpp b/SnM/SnM_Notes.cpp index 1f99e5a8d..bf5612399 100644 --- a/SnM/SnM_Notes.cpp +++ b/SnM/SnM_Notes.cpp @@ -126,6 +126,7 @@ MediaTrack* g_trNote = NULL; // to distinguish internal marker/region updates from external ones bool g_internalMkrRgnChange = false; +static bool DoImportSubTitleFile(const char *fn); /////////////////////////////////////////////////////////////////////////////// // NotesWnd @@ -486,6 +487,19 @@ void NotesWnd::OnTimer(WPARAM wParam) } } +void NotesWnd::OnDroppedFiles(HDROP _h) +{ + if (g_locked || !(g_notesType == SNM_NOTES_MKR_SUB || g_notesType == SNM_NOTES_RGN_SUB || g_notesType == SNM_NOTES_MKRRGN_SUB)) + return; + const int iFiles = DragQueryFile(_h, 0xFFFFFFFF, NULL, 0); + char fn[SNM_MAX_PATH]; + for (int i = 0; i < iFiles; i++) + { + if (DragQueryFile(_h, i, fn, sizeof(fn)) && !DoImportSubTitleFile(fn)) + break; + } +} + void NotesWnd::OnResize() { if (g_notesType != g_prevNotesType) @@ -1415,13 +1429,16 @@ bool ImportAdvancedSubStationFile(const char* _fn) WDL_FastString notes; if (*commaPos != ',') { // if Name field isn't empty notes.Append("["); - char* nextCommaPos = strchr(commaPos, ','); - while (!nextCommaPos && !strchr(buf, '\n') && fgets(buf, sizeof(buf), f) && *buf) { // in case comma + char* nextCommaPos; + while (!(nextCommaPos = strchr(commaPos, ',')) && !strchr(buf, '\n')) { notes.Append(commaPos); commaPos = buf; - nextCommaPos = strchr(buf, ','); + if (!fgets(buf, sizeof(buf), f) || !*buf) + break; } - char nameBuf[1024]; + if (!nextCommaPos) + break; + char nameBuf[sizeof(buf)]; memcpy(nameBuf, commaPos, nextCommaPos-commaPos); nameBuf[nextCommaPos-commaPos] = '\0'; notes.Append(nameBuf); @@ -1429,7 +1446,7 @@ bool ImportAdvancedSubStationFile(const char* _fn) commaPos = nextCommaPos; } commaCount++; - while (commaCount < 9 && !(*commaPos == '\n' || *commaPos == '\r')) { + while (commaCount < 9 && commaPos && !(*commaPos == '\n' || *commaPos == '\r')) { commaPos = strchr(commaPos+1, ','); while (commaPos == nullptr) { if (!(fgets(buf, sizeof(buf), f) && *buf && !(*buf == '\n' || *buf == '\r'))) { @@ -1442,9 +1459,8 @@ bool ImportAdvancedSubStationFile(const char* _fn) } commaCount++; } - if (commaCount != 9) { + if (commaCount != 9 || !commaPos) break; - } char *textPos = commaPos + 1; char text[1024]; @@ -1547,7 +1563,7 @@ bool ImportAdvancedSubStationFile(const char* _fn) return ok; } -static bool ImportSubTitleFile(const char *fn) +bool DoImportSubTitleFile(const char *fn) { constexpr struct { const char *ext; bool (*reader)(const char *); } formats[] { { "srt", &ImportSubRipFile }, @@ -1578,7 +1594,7 @@ void ImportSubTitleFile(COMMAND_T* _ct) if (char* fn = BrowseForFiles(__LOCALIZE("S&M - Import subtitle file","sws_DLG_152"), g_lastImportSubFn, NULL, false, SNM_SUB_IMPORT_EXT_LIST)) { lstrcpyn(g_lastImportSubFn, fn, sizeof(g_lastImportSubFn)); - ImportSubTitleFile(fn); + DoImportSubTitleFile(fn); free(fn); } } @@ -2020,15 +2036,3 @@ void NF_DoUpdateSWSMarkerRegionSubWindow() w->ForceUpdateMkrRgnNameOrSub(g_notesType); } } - -void NotesWnd::OnDroppedFiles(HDROP _h) { - if (g_locked || !(g_notesType == SNM_NOTES_MKR_SUB || g_notesType == SNM_NOTES_RGN_SUB || g_notesType == SNM_NOTES_MKRRGN_SUB)) - return; - const int iFiles = DragQueryFile(_h, 0xFFFFFFFF, NULL, 0); - char fn[SNM_MAX_PATH]; - for (int i = 0; i < iFiles; i++) - { - if (DragQueryFile(_h, i, fn, sizeof(fn)) && !ImportSubTitleFile(fn)) - break; - } -}