From f9d9fee31448d0d98b35456f63931a387208e6a4 Mon Sep 17 00:00:00 2001 From: def Date: Tue, 8 Jan 2019 20:12:21 +0100 Subject: [PATCH] Demo browser: Show markers (fixes #329) - Fetch Headers button - Display Markers & Length columns - Display footer info - Don't rescan directory when not required - Clean up code a bit --- src/base/tl/sorted_array.h | 18 ++-- src/engine/demo.h | 2 +- src/engine/shared/config_variables.h | 2 +- src/engine/shared/demo.cpp | 6 +- src/engine/shared/demo.h | 2 +- src/game/client/components/menus.h | 83 ++++++++++------ src/game/client/components/menus_demo.cpp | 109 ++++++++++++++-------- 7 files changed, 140 insertions(+), 82 deletions(-) diff --git a/src/base/tl/sorted_array.h b/src/base/tl/sorted_array.h index 72e7d5eb5..1bc77e881 100644 --- a/src/base/tl/sorted_array.h +++ b/src/base/tl/sorted_array.h @@ -23,15 +23,15 @@ public: return parent::insert(item, partition_binary(all(), item)); } - int add_unsorted(const T& item) - { - return parent::add(item); - } - - void sort_range() - { - sort(all()); - } + int add_unsorted(const T& item) + { + return parent::add(item); + } + + void sort_range() + { + sort(all()); + } /* diff --git a/src/engine/demo.h b/src/engine/demo.h index 7553f03af..5854351b7 100644 --- a/src/engine/demo.h +++ b/src/engine/demo.h @@ -67,7 +67,7 @@ public: virtual bool IsPlaying() const = 0; virtual const CInfo *BaseInfo() const = 0; virtual void GetDemoName(char *pBuffer, int BufferSize) const = 0; - virtual bool GetDemoInfo(class IStorage *pStorage, const char *pFilename, int StorageType, CDemoHeader *pDemoHeader) const = 0; + virtual bool GetDemoInfo(class IStorage *pStorage, const char *pFilename, int StorageType, CDemoHeader *pDemoHeader, CTimelineMarkers *pTimelineMarkers) const = 0; virtual int GetDemoType() const = 0; }; diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h index c05e9e162..2c4f64d39 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -61,7 +61,7 @@ MACRO_CONFIG_INT(BrSort, br_sort, 4, 0, 256, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") MACRO_CONFIG_INT(BrSortOrder, br_sort_order, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") MACRO_CONFIG_INT(BrMaxRequests, br_max_requests, 25, 0, 1000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Number of requests to use when refreshing server browser") -MACRO_CONFIG_INT(BrDemoSort, br_demo_sort, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") +MACRO_CONFIG_INT(BrDemoSort, br_demo_sort, 0, 0, 3, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") MACRO_CONFIG_INT(BrDemoSortOrder, br_demo_sort_order, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") MACRO_CONFIG_INT(SndBufferSize, snd_buffer_size, 512, 128, 32768, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound buffer size") diff --git a/src/engine/shared/demo.cpp b/src/engine/shared/demo.cpp index 395eaa656..c4b4034e5 100644 --- a/src/engine/shared/demo.cpp +++ b/src/engine/shared/demo.cpp @@ -914,18 +914,20 @@ void CDemoPlayer::GetDemoName(char *pBuffer, int BufferSize) const str_copy(pBuffer, pExtractedName, Length); } -bool CDemoPlayer::GetDemoInfo(class IStorage *pStorage, const char *pFilename, int StorageType, CDemoHeader *pDemoHeader) const +bool CDemoPlayer::GetDemoInfo(class IStorage *pStorage, const char *pFilename, int StorageType, CDemoHeader *pDemoHeader, CTimelineMarkers *pTimelineMarkers) const { - if(!pDemoHeader) + if(!pDemoHeader || !pTimelineMarkers) return false; mem_zero(pDemoHeader, sizeof(CDemoHeader)); + mem_zero(pTimelineMarkers, sizeof(CTimelineMarkers)); IOHANDLE File = pStorage->OpenFile(pFilename, IOFLAG_READ, StorageType); if(!File) return false; io_read(File, pDemoHeader, sizeof(CDemoHeader)); + io_read(File, pTimelineMarkers, sizeof(CTimelineMarkers)); io_close(File); return !(mem_comp(pDemoHeader->m_aMarker, gs_aHeaderMarker, sizeof(gs_aHeaderMarker)) || pDemoHeader->m_Version < gs_OldVersion); } diff --git a/src/engine/shared/demo.h b/src/engine/shared/demo.h index 5de604686..6394a4073 100644 --- a/src/engine/shared/demo.h +++ b/src/engine/shared/demo.h @@ -135,7 +135,7 @@ public: int SetPos(float Percent); const CInfo *BaseInfo() const { return &m_Info.m_Info; } void GetDemoName(char *pBuffer, int BufferSize) const; - bool GetDemoInfo(class IStorage *pStorage, const char *pFilename, int StorageType, CDemoHeader *pDemoHeader) const; + bool GetDemoInfo(class IStorage *pStorage, const char *pFilename, int StorageType, CDemoHeader *pDemoHeader, CTimelineMarkers *pTimelineMarkers) const; const char *GetDemoFileName() { return m_aFilename; }; int GetDemoType() const; diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h index 8303fc299..26de2a439 100644 --- a/src/game/client/components/menus.h +++ b/src/game/client/components/menus.h @@ -152,6 +152,14 @@ class CMenus : public CComponent char m_aFilterString[25]; // demo + enum + { + SORT_DEMONAME=0, + SORT_MARKERS, + SORT_LENGTH, + SORT_DATE, + }; + struct CDemoItem { char m_aFilename[128]; @@ -163,39 +171,51 @@ class CMenus : public CComponent bool m_InfosLoaded; bool m_Valid; CDemoHeader m_Info; + CTimelineMarkers m_TimelineMarkers; - bool operator<(const CDemoItem &Other) + int NumMarkers() const { - if (g_Config.m_BrDemoSort) - { - if (g_Config.m_BrDemoSortOrder) - { - return !str_comp(m_aFilename, "..") ? true : !str_comp(Other.m_aFilename, "..") ? false : - m_IsDir && !Other.m_IsDir ? true : !m_IsDir && Other.m_IsDir ? false : - m_Date < Other.m_Date; - } - else - { - return !str_comp(m_aFilename, "..") ? true : !str_comp(Other.m_aFilename, "..") ? false : - m_IsDir && !Other.m_IsDir ? true : !m_IsDir && Other.m_IsDir ? false : - m_Date > Other.m_Date; - } - } - else - { - if (g_Config.m_BrDemoSortOrder) - { - return !str_comp(m_aFilename, "..") ? true : !str_comp(Other.m_aFilename, "..") ? false : - m_IsDir && !Other.m_IsDir ? true : !m_IsDir && Other.m_IsDir ? false : - str_comp_nocase(m_aFilename, Other.m_aFilename) < 0; - } - else - { - return !str_comp(m_aFilename, "..") ? true : !str_comp(Other.m_aFilename, "..") ? false : - m_IsDir && !Other.m_IsDir ? true : !m_IsDir && Other.m_IsDir ? false : - str_comp_nocase(m_aFilename, Other.m_aFilename) > 0; - } - } + return ((m_TimelineMarkers.m_aNumTimelineMarkers[0]<<24)&0xFF000000) | ((m_TimelineMarkers.m_aNumTimelineMarkers[1]<<16)&0xFF0000) | + ((m_TimelineMarkers.m_aNumTimelineMarkers[2]<<8)&0xFF00) | (m_TimelineMarkers.m_aNumTimelineMarkers[3]&0xFF); + } + + int Length() const + { + return ((m_Info.m_aLength[0]<<24)&0xFF000000) | ((m_Info.m_aLength[1]<<16)&0xFF0000) | + ((m_Info.m_aLength[2]<<8)&0xFF00) | (m_Info.m_aLength[3]&0xFF); + } + + bool operator<(const CDemoItem &Other) const + { + if(!str_comp(m_aFilename, "..")) + return true; + if(!str_comp(Other.m_aFilename, "..")) + return false; + if(m_IsDir && !Other.m_IsDir) + return true; + if(!m_IsDir && Other.m_IsDir) + return false; + + const CDemoItem &Left = g_Config.m_BrDemoSortOrder ? Other : *this; + const CDemoItem &Right = g_Config.m_BrDemoSortOrder ? *this : Other; + + if(g_Config.m_BrDemoSort == SORT_DEMONAME) + return str_comp_nocase(Left.m_aFilename, Right.m_aFilename) < 0; + if(g_Config.m_BrDemoSort == SORT_DATE) + return Left.m_Date < Right.m_Date; + + if(!Other.m_InfosLoaded) + return m_InfosLoaded; + if(!m_InfosLoaded) + return !Other.m_InfosLoaded; + + if(g_Config.m_BrDemoSort == SORT_MARKERS) + return Left.NumMarkers() < Right.NumMarkers(); + if(g_Config.m_BrDemoSort == SORT_LENGTH) + return Left.Length() < Right.Length(); + + // Unknown sort + return true; } }; @@ -247,6 +267,7 @@ class CMenus : public CComponent // found in menus_demo.cpp static bool DemoFilterChat(const void *pData, int Size, void *pUser); + bool FetchHeader(CDemoItem &Item); void RenderDemoPlayer(CUIRect MainView); void RenderDemoList(CUIRect MainView); diff --git a/src/game/client/components/menus_demo.cpp b/src/game/client/components/menus_demo.cpp index a2900f16b..4053f0422 100644 --- a/src/game/client/components/menus_demo.cpp +++ b/src/game/client/components/menus_demo.cpp @@ -419,7 +419,6 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) if(DoButton_DemoPlayer(&s_ExitButton, Localize("Close"), 0, &Button)) { Client()->Disconnect(); - DemolistPopulate(); DemolistOnUpdate(false); } @@ -744,6 +743,18 @@ void CMenus::DemolistOnUpdate(bool Reset) m_DemolistSelectedIsDir = m_DemolistSelectedIndex < 0 ? false : m_lDemos[m_DemolistSelectedIndex].m_IsDir; } +bool CMenus::FetchHeader(CDemoItem &Item) +{ + if(!Item.m_InfosLoaded) + { + char aBuffer[512]; + str_format(aBuffer, sizeof(aBuffer), "%s/%s", m_aCurrentDemoFolder, Item.m_aFilename); + Item.m_Valid = DemoPlayer()->GetDemoInfo(Storage(), aBuffer, Item.m_StorageType, &Item.m_Info, &Item.m_TimelineMarkers); + Item.m_InfosLoaded = true; + } + return Item.m_Valid; +} + void CMenus::RenderDemoList(CUIRect MainView) { static int s_Inited = 0; @@ -757,40 +768,34 @@ void CMenus::RenderDemoList(CUIRect MainView) char aFooterLabel[128] = {0}; if(m_DemolistSelectedIndex >= 0) { - CDemoItem *Item = &m_lDemos[m_DemolistSelectedIndex]; - if(str_comp(Item->m_aFilename, "..") == 0) + CDemoItem &Item = m_lDemos[m_DemolistSelectedIndex]; + if(str_comp(Item.m_aFilename, "..") == 0) str_copy(aFooterLabel, Localize("Parent Folder"), sizeof(aFooterLabel)); else if(m_DemolistSelectedIsDir) str_copy(aFooterLabel, Localize("Folder"), sizeof(aFooterLabel)); + else if(!FetchHeader(Item)) + str_copy(aFooterLabel, Localize("Invalid Demo"), sizeof(aFooterLabel)); else - { - if(!Item->m_InfosLoaded) - { - char aBuffer[512]; - str_format(aBuffer, sizeof(aBuffer), "%s/%s", m_aCurrentDemoFolder, Item->m_aFilename); - Item->m_Valid = DemoPlayer()->GetDemoInfo(Storage(), aBuffer, Item->m_StorageType, &Item->m_Info); - Item->m_InfosLoaded = true; - } - if(!Item->m_Valid) - str_copy(aFooterLabel, Localize("Invalid Demo"), sizeof(aFooterLabel)); - else - str_copy(aFooterLabel, Localize("Demo details"), sizeof(aFooterLabel)); - } + str_copy(aFooterLabel, Localize("Demo details"), sizeof(aFooterLabel)); } // render background RenderTools()->DrawUIRect(&MainView, ms_ColorTabbarActive, CUI::CORNER_ALL, 10.0f); MainView.Margin(10.0f, &MainView); - CUIRect ButtonBar, RefreshRect, PlayRect, DeleteRect, RenameRect, ListBox; + CUIRect ButtonBar, RefreshRect, FetchRect, PlayRect, DeleteRect, RenameRect, LabelRect, ListBox; MainView.HSplitBottom(ms_ButtonHeight+5.0f, &MainView, &ButtonBar); ButtonBar.HSplitTop(5.0f, 0, &ButtonBar); ButtonBar.VSplitRight(130.0f, &ButtonBar, &PlayRect); ButtonBar.VSplitLeft(130.0f, &RefreshRect, &ButtonBar); ButtonBar.VSplitLeft(10.0f, 0, &ButtonBar); + ButtonBar.VSplitLeft(130.0f, &FetchRect, &ButtonBar); + ButtonBar.VSplitLeft(10.0f, 0, &ButtonBar); ButtonBar.VSplitLeft(120.0f, &DeleteRect, &ButtonBar); ButtonBar.VSplitLeft(10.0f, 0, &ButtonBar); ButtonBar.VSplitLeft(120.0f, &RenameRect, &ButtonBar); + ButtonBar.VSplitLeft(10.0f, 0, &ButtonBar); + ButtonBar.VSplitLeft(120.0f, &LabelRect, &ButtonBar); MainView.HSplitBottom(140.0f, &ListBox, &MainView); // render demo info @@ -800,7 +805,8 @@ void CMenus::RenderDemoList(CUIRect MainView) if(!m_DemolistSelectedIsDir && m_DemolistSelectedIndex >= 0 && m_lDemos[m_DemolistSelectedIndex].m_Valid) { CUIRect Left, Right, Labels; - MainView.Margin(20.0f, &MainView); + MainView.VMargin(20.0f, &MainView); + MainView.HMargin(10.0f, &MainView); MainView.VSplitMid(&Labels, &MainView); // left side @@ -821,8 +827,7 @@ void CMenus::RenderDemoList(CUIRect MainView) Labels.HSplitTop(20.0f, &Left, &Labels); Left.VSplitLeft(150.0f, &Left, &Right); UI()->DoLabelScaled(&Left, Localize("Length:"), 14.0f, -1); - int Length = ((m_lDemos[m_DemolistSelectedIndex].m_Info.m_aLength[0]<<24)&0xFF000000) | ((m_lDemos[m_DemolistSelectedIndex].m_Info.m_aLength[1]<<16)&0xFF0000) | - ((m_lDemos[m_DemolistSelectedIndex].m_Info.m_aLength[2]<<8)&0xFF00) | (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aLength[3]&0xFF); + int Length = m_lDemos[m_DemolistSelectedIndex].Length(); char aBuf[64]; str_format(aBuf, sizeof(aBuf), "%d:%02d", Length/60, Length%60); UI()->DoLabelScaled(&Right, aBuf, 14.0f, -1); @@ -832,6 +837,12 @@ void CMenus::RenderDemoList(CUIRect MainView) UI()->DoLabelScaled(&Left, Localize("Version:"), 14.0f, -1); str_format(aBuf, sizeof(aBuf), "%d", m_lDemos[m_DemolistSelectedIndex].m_Info.m_Version); UI()->DoLabelScaled(&Right, aBuf, 14.0f, -1); + Labels.HSplitTop(5.0f, 0, &Labels); + Labels.HSplitTop(20.0f, &Left, &Labels); + Left.VSplitLeft(150.0f, &Left, &Right); + UI()->DoLabelScaled(&Left, Localize("Markers:"), 14.0f, -1); + str_format(aBuf, sizeof(aBuf), "%d", m_lDemos[m_DemolistSelectedIndex].NumMarkers()); + UI()->DoLabelScaled(&Right, aBuf, 14.0f, -1); // right side Labels = MainView; @@ -841,8 +852,7 @@ void CMenus::RenderDemoList(CUIRect MainView) UI()->DoLabelScaled(&Right, m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapName, 14.0f, -1); Labels.HSplitTop(5.0f, 0, &Labels); Labels.HSplitTop(20.0f, &Left, &Labels); - Left.VSplitLeft(20.0f, 0, &Left); - Left.VSplitLeft(130.0f, &Left, &Right); + Left.VSplitLeft(150.0f, &Left, &Right); UI()->DoLabelScaled(&Left, Localize("Size:"), 14.0f, -1); unsigned Size = (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapSize[0]<<24) | (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapSize[1]<<16) | (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapSize[2]<<8) | (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapSize[3]); @@ -853,8 +863,7 @@ void CMenus::RenderDemoList(CUIRect MainView) UI()->DoLabelScaled(&Right, aBuf, 14.0f, -1); Labels.HSplitTop(5.0f, 0, &Labels); Labels.HSplitTop(20.0f, &Left, &Labels); - Left.VSplitLeft(20.0f, 0, &Left); - Left.VSplitLeft(130.0f, &Left, &Right); + Left.VSplitLeft(150.0f, &Left, &Right); UI()->DoLabelScaled(&Left, Localize("Crc:"), 14.0f, -1); unsigned Crc = (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapCrc[0]<<24) | (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapCrc[1]<<16) | (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapCrc[2]<<8) | (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapCrc[3]); @@ -890,16 +899,17 @@ void CMenus::RenderDemoList(CUIRect MainView) { COL_ICON=0, COL_DEMONAME, + COL_MARKERS, + COL_LENGTH, COL_DATE, - - SORT_DEMONAME=0, - SORT_DATE, }; static CColumn s_aCols[] = { - {COL_ICON, -1, " ", -1, 14.0f, 0, {0}, {0}}, - {COL_DEMONAME, SORT_DEMONAME, Localize("Demo"), 0, 0.0f, 0, {0}, {0}}, - {COL_DATE, SORT_DATE, Localize("Date"), 1, 300.0f, 0, {0}, {0}}, + {COL_ICON, -1, " ", -1, 14.0f, 0, {0}, {0}}, + {COL_DEMONAME, SORT_DEMONAME, Localize("Demo"), 0, 0.0f, 0, {0}, {0}}, + {COL_MARKERS, SORT_MARKERS, Localize("Markers"), 1, 75.0f, 0, {0}, {0}}, + {COL_LENGTH, SORT_LENGTH, Localize("Length"), 1, 75.0f, 0, {0}, {0}}, + {COL_DATE, SORT_DATE, Localize("Date"), 1, 160.0f, 1, {0}, {0}}, }; RenderTools()->DrawUIRect(&Headers, vec4(0.0f,0,0,0.15f), 0, 0); @@ -950,7 +960,8 @@ void CMenus::RenderDemoList(CUIRect MainView) g_Config.m_BrDemoSort = s_aCols[i].m_Sort; } - DemolistPopulate(); + // Don't rescan in order to keep fetched headers, just resort + m_lDemos.sort_range(); DemolistOnUpdate(false); } } @@ -1115,15 +1126,27 @@ void CMenus::RenderDemoList(CUIRect MainView) TextRender()->TextEx(&Cursor, r.front().m_aName, -1); } + else if(ID == COL_MARKERS && !r.front().m_IsDir && r.front().m_InfosLoaded) + { + char aBuf[3]; + str_format(aBuf, sizeof(aBuf), "%d", r.front().NumMarkers()); + Button.VMargin(4.0f, &Button); + UI()->DoLabelScaled(&Button, aBuf, 12.0f, 1); + } + else if(ID == COL_LENGTH && !r.front().m_IsDir && r.front().m_InfosLoaded) + { + int Length = r.front().Length(); + char aBuf[32]; + str_format(aBuf, sizeof(aBuf), "%d:%02d", Length/60, Length%60); + Button.VMargin(4.0f, &Button); + UI()->DoLabelScaled(&Button, aBuf, 12.0f, 1); + } else if(ID == COL_DATE && !r.front().m_IsDir) { - CTextCursor Cursor; - TextRender()->SetCursor(&Cursor, Button.x, Button.y + (Button.h - 12.0f * UI()->Scale()) / 2.f, 12.0f * UI()->Scale(), TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); - Cursor.m_LineWidth = Button.w; - - char aBuf[256]; + char aBuf[64]; str_timestamp_ex(r.front().m_Date, aBuf, sizeof(aBuf), FORMAT_SPACE); - TextRender()->TextEx(&Cursor, aBuf, -1); + Button.VSplitRight(24.0f, &Button, 0); + UI()->DoLabelScaled(&Button, aBuf, 12.0f, 1); } } } @@ -1150,6 +1173,16 @@ void CMenus::RenderDemoList(CUIRect MainView) DemolistOnUpdate(false); } + static int s_FetchButton = 0; + if(DoButton_Menu(&s_FetchButton, Localize("Fetch Info"), 0, &FetchRect) || (Input()->KeyPress(KEY_F) && (Input()->KeyIsPressed(KEY_LCTRL) || Input()->KeyIsPressed(KEY_RCTRL)))) + { + for(sorted_array::range r = m_lDemos.all(); !r.empty(); r.pop_front()) + { + FetchHeader(r.front()); + } + m_lDemos.sort_range(); + } + static int s_PlayButton = 0; if(DoButton_Menu(&s_PlayButton, m_DemolistSelectedIsDir?Localize("Open"):Localize("Play"), 0, &PlayRect) || Activated) { @@ -1210,4 +1243,6 @@ void CMenus::RenderDemoList(CUIRect MainView) } } } + + UI()->DoLabelScaled(&LabelRect, aFooterLabel, 14.0f, -1); }