OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
C4StartupScenSelDlg.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2005-2009, RedWolf Design GmbH, http://www.clonk.de/
5  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
6  *
7  * Distributed under the terms of the ISC license; see accompanying file
8  * "COPYING" for details.
9  *
10  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11  * See accompanying file "TRADEMARK" for details.
12  *
13  * To redistribute this file separately, substitute the full license texts
14  * for the above references.
15  */
16 // Startup screen for non-parameterized engine start: Scenario selection dialog
17 
18 #include "C4Include.h"
20 
22 #include "c4group/C4Components.h"
23 #include "game/C4Application.h"
24 #include "graphics/C4Draw.h"
26 #include "gui/C4FileSelDlg.h"
27 #include "gui/C4GameDialogs.h"
28 #include "gui/C4GameOptions.h"
29 #include "gui/C4MouseControl.h"
30 #include "gui/C4StartupMainDlg.h"
31 #include "gui/C4StartupNetDlg.h"
33 
34 // singleton
36 
37 
38 // ----------------------------------------------------------------
39 // Map folder data
40 
42 {
43  pComp->Value(mkNamingAdapt( sFilename, "File" , StdStrBuf()));
44  pComp->Value(mkNamingAdapt( sBaseImage, "BaseImage" , StdStrBuf()));
45  pComp->Value(mkNamingAdapt( sOverlayImage, "OverlayImage" , StdStrBuf()));
46  pComp->Value(mkNamingAdapt( rcOverlayPos, "Area", C4Rect()));
47  pComp->Value(mkNamingAdapt( sTitle, "Title" , StdStrBuf()));
48  pComp->Value(mkNamingAdapt( iTitleFontSize, "TitleFontSize", 20));
49  pComp->Value(mkNamingAdapt( dwTitleInactClr, "TitleColorInactive", 0x7fffffffu));
50  pComp->Value(mkNamingAdapt( dwTitleActClr, "TitleColorActive", 0x0fffffffu));
51  pComp->Value(mkNamingAdapt( iTitleOffX, "TitleOffX", 0));
52  pComp->Value(mkNamingAdapt( iTitleOffY, "TitleOffY", 0));
53  pComp->Value(mkNamingAdapt( byTitleAlign, "TitleAlign", ACenter));
54  pComp->Value(mkNamingAdapt( fTitleBookFont, "TitleUseBookFont", true));
55  pComp->Value(mkNamingAdapt( fImgDump, "ImageDump", false)); // developer help
56 }
57 
59 {
60  pComp->Value(mkNamingAdapt( sPassword, "Access", StdStrBuf()));
61  pComp->Value(mkNamingAdapt( sOverlayImage, "OverlayImage" , StdStrBuf()));
62  pComp->Value(mkNamingAdapt( rcOverlayPos, "Area", C4Rect()));
63 }
64 
65 C4MapFolderData::MapPic::MapPic(const FLOAT_RECT &rcfBounds, const C4Facet &rfct) : C4GUI::Picture(C4Rect(rcfBounds), false), rcfBounds(rcfBounds)
66 {
67  // ctor
68  SetFacet(rfct);
69  SetToolTip(LoadResStr("IDS_MSG_MAP_DESC"));
70 }
71 
72 void C4MapFolderData::MapPic::MouseInput(C4GUI::CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam)
73 {
74  typedef C4GUI::Picture Parent;
75  Parent::MouseInput(rMouse, iButton, iX, iY, dwKeyParam);
76  // input: mouse movement or buttons - deselect everything if clicked
77  if (iButton == C4MC_Button_LeftDown && C4StartupScenSelDlg::pInstance)
78  {
79  C4StartupScenSelDlg::pInstance->DeselectAll();
80  }
81 }
82 
83 void C4MapFolderData::MapPic::DrawElement(C4TargetFacet &cgo)
84 {
85  // get drawing bounds
86  float x0 = rcfBounds.left + cgo.TargetX, y0 = rcfBounds.top + cgo.TargetY;
87  // draw the image
88  GetFacet().DrawXFloat(cgo.Surface, x0, y0, rcfBounds.right-rcfBounds.left, rcfBounds.bottom-rcfBounds.top);
89 }
90 
92 {
93  fCoordinatesAdjusted = false;
94  fctBackgroundPicture.Clear();
95  pScenarioFolder = nullptr;
96  pSelectedEntry = nullptr;
97  pSelectionInfoBox = nullptr;
98  rcScenInfoArea.Set(0,0,0,0);
99  MinResX=MinResY=0;
100  fUseFullscreenMap=false;
101  int i;
102  for (i=0; i<iScenCount; ++i) delete ppScenList[i];
103  iScenCount=0;
104  delete [] ppScenList; ppScenList=nullptr;
105  for (i=0; i<iAccessGfxCount; ++i) delete ppAccessGfxList[i];
106  iAccessGfxCount=0;
107  delete [] ppAccessGfxList; ppAccessGfxList=nullptr;
108  pMainDlg = nullptr;
109 }
110 
112 {
113  // clear previous
114  Clear();
115  // load localization info
116  C4LangStringTable LangTable;
117  bool fHasLangTable = C4Language::LoadComponentHost(&LangTable, hGroup, C4CFN_ScriptStringTbl, Config.General.LanguageEx);
118  // load core data
119  StdStrBuf Buf;
120  if (!hGroup.LoadEntryString(C4CFN_MapFolderData, &Buf)) return false;
121  if (fHasLangTable) LangTable.ReplaceStrings(Buf);
122  if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(mkNamingAdapt(*this, "FolderMap"), Buf, C4CFN_MapFolderData)) return false;
123  // check resolution requirement
124  if (MinResX && MinResX>C4GUI::GetScreenWdt()) return false;
125  if (MinResY && MinResY>C4GUI::GetScreenHgt()) return false;
126  // load images
127  if (!fctBackgroundPicture.Load(hGroup, C4CFN_MapFolderBG, C4FCT_Full, C4FCT_Full, false, 0))
128  {
129  DebugLogF(R"(C4MapFolderData::Load(%s): Could not load background graphic "%s")", hGroup.GetName(), C4CFN_MapFolderBG);
130  return false;
131  }
132  int i;
133  for (i=0; i<iScenCount; ++i)
134  {
135  // init scenario entry stuff
136  Scenario *pScen = ppScenList[i];
137  pScen->pScenEntry = pScenLoaderFolder->FindEntryByName(pScen->sFilename.getData());
138  pScen->pBtn = nullptr;
139  pScen->sTitle.Replace("TITLE", pScen->pScenEntry ? pScen->pScenEntry->GetName().getData() : "<c ff0000>ERROR</c>" /* scenario not loaded; title cannot be referenced */);
140  // developer image dump
141  if (pScen->fImgDump)
142  {
143  C4FacetSurface fctDump; bool fSuccess=false;
144  if (fctDump.Create(pScen->rcOverlayPos.Wdt, pScen->rcOverlayPos.Hgt, C4FCT_Full, C4FCT_Full))
145  {
146  pDraw->Blit(fctBackgroundPicture.Surface,
147  (float) pScen->rcOverlayPos.x, (float) pScen->rcOverlayPos.y,
148  (float) pScen->rcOverlayPos.Wdt, (float) pScen->rcOverlayPos.Hgt,
149  fctDump.Surface,
150  0, 0,
151  fctDump.Wdt, fctDump.Hgt);
152  fSuccess = fctDump.Surface->SavePNG(pScen->sBaseImage.getData(), true, false, false);
153  }
154  if (!fSuccess)
155  DebugLogF(R"(C4MapFolderData::Load(%s): Could not dump graphic "%s")", hGroup.GetName(), pScen->sBaseImage.getData());
156  continue;
157  }
158  // load images
159  if (pScen->sBaseImage.getLength()>0) if (!pScen->fctBase.Load(hGroup, pScen->sBaseImage.getData(), C4FCT_Full, C4FCT_Full, false, 0))
160  {
161  DebugLogF(R"(C4MapFolderData::Load(%s): Could not load base graphic "%s")", hGroup.GetName(), pScen->sBaseImage.getData());
162  return false;
163  }
164  if (pScen->sOverlayImage.getLength()>0) if (!pScen->fctOverlay.Load(hGroup, pScen->sOverlayImage.getData(), C4FCT_Full, C4FCT_Full, false, 0))
165  {
166  DebugLogF(R"(C4MapFolderData::Load(%s): Could not load graphic "%s")", hGroup.GetName(), pScen->sOverlayImage.getData());
167  return false;
168  }
169  }
170  for (i=0; i<iAccessGfxCount; ++i)
171  {
172  AccessGfx *pGfx= ppAccessGfxList[i];
173  if (pGfx->sOverlayImage.getLength()>0) if (!pGfx->fctOverlay.Load(hGroup, pGfx->sOverlayImage.getData(), C4FCT_Full, C4FCT_Full, false, 0))
174  {
175  DebugLogF(R"(C4MapFolderData::Load(%s): Could not load graphic "%s")", hGroup.GetName(), pGfx->sOverlayImage.getData());
176  return false;
177  }
178  }
179  // all loaded
180  pScenarioFolder = pScenLoaderFolder;
181  return true;
182 }
183 
185 {
186  // core values
187  pComp->Value(mkNamingAdapt( rcScenInfoArea, "ScenInfoArea", C4Rect(0,0,0,0)));
188  pComp->Value(mkNamingAdapt( MinResX, "MinResX", 0));
189  pComp->Value(mkNamingAdapt( MinResY, "MinResY", 0));
190  pComp->Value(mkNamingAdapt( fUseFullscreenMap,"FullscreenBG", false));
191  // compile scenario list
192  int32_t iOldScenCount = iScenCount;
193  pComp->Value(mkNamingCountAdapt(iScenCount, "Scenario"));
194  if (pComp->isDeserializer())
195  {
196  while (iOldScenCount--) delete ppScenList[iOldScenCount];
197  delete [] ppScenList;
198  if (iScenCount)
199  {
200  ppScenList = new Scenario *[iScenCount];
201  memset(ppScenList, 0, sizeof(Scenario *)*iScenCount);
202  }
203  else
204  ppScenList = nullptr;
205  }
206  if (iScenCount)
207  {
208  mkPtrAdaptNoNull(*ppScenList);
209  pComp->Value(mkNamingAdapt(mkArrayAdaptMap(ppScenList, iScenCount, mkPtrAdaptNoNull<Scenario>), "Scenario"));
210  }
211  // compile access gfx list
212  int32_t iOldAccesGfxCount = iAccessGfxCount;
213  pComp->Value(mkNamingCountAdapt(iAccessGfxCount, "AccessGfx"));
214  if (pComp->isDeserializer())
215  {
216  while (iOldAccesGfxCount--) delete ppAccessGfxList[iOldAccesGfxCount];
217  delete [] ppAccessGfxList;
218  if (iAccessGfxCount)
219  {
220  ppAccessGfxList = new AccessGfx *[iAccessGfxCount];
221  memset(ppAccessGfxList, 0, sizeof(AccessGfx *)*iAccessGfxCount);
222  }
223  else
224  ppAccessGfxList = nullptr;
225  }
226  if (iAccessGfxCount)
227  {
228  mkPtrAdaptNoNull(*ppAccessGfxList);
229  pComp->Value(mkNamingAdapt(mkArrayAdaptMap(ppAccessGfxList, iAccessGfxCount, mkPtrAdaptNoNull<AccessGfx>), "AccessGfx"));
230  }
231 }
232 
233 void C4MapFolderData::ConvertFacet2ScreenCoord(const C4Rect &rc, FLOAT_RECT *pfrc, float fBGZoomX, float fBGZoomY, int iOffX, int iOffY)
234 {
235  pfrc->left = (fBGZoomX * rc.x) + iOffX;
236  pfrc->top = (fBGZoomY * rc.y) + iOffY;
237  pfrc->right = pfrc->left + (fBGZoomX * rc.Wdt);
238  pfrc->bottom = pfrc->top + (fBGZoomY * rc.Hgt);
239 }
240 
241 void C4MapFolderData::ConvertFacet2ScreenCoord(int32_t *piValue, float fBGZoom, int iOff)
242 {
243  *piValue = int32_t(floorf(fBGZoom * *piValue + 0.5f)) + iOff;
244 }
245 
246 void C4MapFolderData::ConvertFacet2ScreenCoord(C4Rect &rcMapArea, bool fAspect)
247 {
248  if (!fctBackgroundPicture.Wdt || !fctBackgroundPicture.Hgt) return; // invalid BG - should not happen
249  // get zoom of background image
250  float fBGZoomX = 1.0f, fBGZoomY = 1.0f; int iOffX=0, iOffY=0;
251  if (fAspect)
252  {
253  if (fctBackgroundPicture.Wdt * rcMapArea.Hgt > rcMapArea.Wdt * fctBackgroundPicture.Hgt)
254  {
255  // background image is limited by width
256  fBGZoomX = fBGZoomY = (float) rcMapArea.Wdt / fctBackgroundPicture.Wdt;
257  iOffY = std::max<int>(0, (int)(rcMapArea.Hgt - (fBGZoomX * fctBackgroundPicture.Hgt)))/2;
258  }
259  else
260  {
261  // background image is limited by height
262  fBGZoomX = fBGZoomY = (float) rcMapArea.Hgt / fctBackgroundPicture.Hgt;
263  iOffX = std::max<int>(0, (int)(rcMapArea.Wdt - (fBGZoomY * fctBackgroundPicture.Wdt)))/2;
264  }
265  }
266  else
267  {
268  // do not keep aspect: Independant X and Y zoom
269  fBGZoomX = (float) rcMapArea.Wdt / fctBackgroundPicture.Wdt;;
270  fBGZoomY = (float) rcMapArea.Hgt / fctBackgroundPicture.Hgt;;
271  }
272  iOffX -= rcMapArea.x; iOffY -= rcMapArea.y;
273  C4Rect rcBG; rcBG.Set(0,0,fctBackgroundPicture.Wdt, fctBackgroundPicture.Hgt);
274  ConvertFacet2ScreenCoord(rcBG, &rcfBG, fBGZoomX, fBGZoomY, iOffX, iOffY);
275  // default for scenario info area: 1/3rd of right area
276  if (!rcScenInfoArea.Wdt)
277  rcScenInfoArea.Set((int32_t)(fctBackgroundPicture.Wdt*2/3), (int32_t)(fctBackgroundPicture.Hgt/16), (int32_t)(fctBackgroundPicture.Wdt/3), (int32_t)(fctBackgroundPicture.Hgt*7/8));
278  // assume all facet coordinates are referring to background image zoom; convert them to screen coordinates by applying zoom and offset
279  FLOAT_RECT rcfScenInfoArea;
280  ConvertFacet2ScreenCoord(rcScenInfoArea, &rcfScenInfoArea, fBGZoomX, fBGZoomY, iOffX, iOffY);
281  rcScenInfoArea.x = (int32_t) rcfScenInfoArea.left; rcScenInfoArea.y = (int32_t) rcfScenInfoArea.top;
282  rcScenInfoArea.Wdt = (int32_t) (rcfScenInfoArea.right - rcfScenInfoArea.left);
283  rcScenInfoArea.Hgt = (int32_t) (rcfScenInfoArea.bottom - rcfScenInfoArea.top);
284  int i;
285  for (i=0; i<iScenCount; ++i)
286  {
287  Scenario *pScen = ppScenList[i];
288  ConvertFacet2ScreenCoord(pScen->rcOverlayPos, &(pScen->rcfOverlayPos), fBGZoomX, fBGZoomY, iOffX, iOffY);
289  // title sizes
290  ConvertFacet2ScreenCoord(&(pScen->iTitleFontSize), fBGZoomY, 0);
291  // title position: Relative to title rect; so do not add offset here
292  ConvertFacet2ScreenCoord(&(pScen->iTitleOffX), fBGZoomX, 0);
293  ConvertFacet2ScreenCoord(&(pScen->iTitleOffY), fBGZoomY, 0);
294  }
295  for (i=0; i<iAccessGfxCount; ++i) ConvertFacet2ScreenCoord(ppAccessGfxList[i]->rcOverlayPos, &(ppAccessGfxList[i]->rcfOverlayPos), fBGZoomX, fBGZoomY, iOffX, iOffY);
296  // done
297  fCoordinatesAdjusted = true;
298 }
299 
301 {
302  this->pMainDlg = pMainDlg;
303  // convert all coordinates to match the container sizes
304  // do this only once; assume container won't change between loads
305  if (!fCoordinatesAdjusted)
306  {
307  if (!fUseFullscreenMap)
308  ConvertFacet2ScreenCoord(rContainer.GetClientRect(), true);
309  else
310  {
311  C4Rect rcMapRect = pMainDlg->GetBounds();
312  rContainer.ClientPos2ScreenPos(rcMapRect.x, rcMapRect.y);
313  ConvertFacet2ScreenCoord(rcMapRect, false);
314  }
315  }
316  // empty any previous stuff in container
317  while (rContainer.GetFirst()) delete rContainer.GetFirst();
318  // create background image
319  if (!fUseFullscreenMap)
320  rContainer.AddElement(new MapPic(rcfBG, fctBackgroundPicture));
321  else
322  {
323  pMainDlg->SetBackground(&fctBackgroundPicture);
324  }
325  // create mission access overlays
326  int i;
327  for (i=0; i<iAccessGfxCount; ++i)
328  {
329  AccessGfx *pGfx = ppAccessGfxList[i];
330  const char *szPassword = pGfx->sPassword.getData();
331  if (!szPassword || !*szPassword || SIsModule(Config.General.MissionAccess, szPassword))
332  {
333  // ACCESS TO GFX GRANTED: draw it
334  rContainer.AddElement(new MapPic(pGfx->rcfOverlayPos, pGfx->fctOverlay));
335  }
336  }
337  // create buttons for scenarios
338  C4GUI::Button *pBtnFirst = nullptr;
339  for (i=0; i<iScenCount; ++i)
340  {
341  Scenario *pScen = ppScenList[i];
342  if (pScen->pScenEntry && !pScen->pScenEntry->HasMissionAccess())
343  {
344  // no access to this scenario: Do not create a button at all; not even base image. The scenario is "invisible".
345  }
346  else
347  {
349  (pScen->fctBase, pScen->fctOverlay, pScen->rcfOverlayPos, 0, pMainDlg, &C4StartupScenSelDlg::OnButtonScenario);
350  ppScenList[i]->pBtn = pBtn;
351  if (pScen->pScenEntry)
352  pBtn->SetToolTip(FormatString(LoadResStr("IDS_MSG_MAP_STARTSCEN"), pScen->pScenEntry->GetName().getData()).getData());
353  if (pScen->sTitle.getLength()>0)
354  {
355  pBtn->SetText(pScen->sTitle.getData());
356  pBtn->SetTextColors(pScen->dwTitleInactClr, pScen->dwTitleActClr);
357  pBtn->SetTextPos(pScen->iTitleOffX, pScen->iTitleOffY, pScen->byTitleAlign);
358  CStdFont *pUseFont; float fFontZoom=1.0f;
359  if (pScen->fTitleBookFont)
360  pUseFont = &(C4Startup::Get()->Graphics.GetBlackFontByHeight(pScen->iTitleFontSize, &fFontZoom));
361  else
362  pUseFont = &(::GraphicsResource.GetFontByHeight(pScen->iTitleFontSize, &fFontZoom));
363  if (Inside<float>(fFontZoom, 0.8f, 1.25f)) fFontZoom = 1.0f; // some tolerance for font zoom
364  pBtn->SetTextFont(pUseFont, fFontZoom);
365  }
366  rContainer.AddElement(pBtn);
367  if (!pBtnFirst) pBtnFirst = pBtn;
368  }
369  }
370  // create scenario info listbox
371  pSelectionInfoBox = new C4GUI::TextWindow(rcScenInfoArea,
373  C4StartupScenSel_TitlePicturePadding, 100, 4096, nullptr, true, &C4Startup::Get()->Graphics.fctScenSelTitleOverlay, C4StartupScenSel_TitleOverlayMargin);
374  pSelectionInfoBox->SetDecoration(false, false, &C4Startup::Get()->Graphics.sfctBookScroll, true);
375  rContainer.AddElement(pSelectionInfoBox);
376 }
377 
379 {
380  // get associated scenario entry
381  int i;
382  for (i=0; i<iScenCount; ++i)
383  if (pEl == ppScenList[i]->pBtn)
384  break;
385  if (i == iScenCount) return;
386  // select the associated entry
387  pSelectedEntry = ppScenList[i]->pScenEntry;
388 }
389 
391 {
392  pSelectedEntry = nullptr;
393 }
394 
395 
396 
397 // ----------------------------------------------------------------
398 // Scenario list loader
399 
400 // ------------------------------------
401 // Entry
402 
403 C4ScenarioListLoader::Entry::Entry(class C4ScenarioListLoader *pLoader, Folder *pParent) : pLoader(pLoader), pNext(nullptr), pParent(pParent), fBaseLoaded(false), fExLoaded(false)
404 {
405  // ctor: Put into parent tree node
406  if (pParent)
407  {
408  pNext = pParent->pFirst;
409  pParent->pFirst = this;
410  }
411  iIconIndex = -1;
412  iDifficulty = 0;
413  iFolderIndex = 0;
414 }
415 
417 {
418  // dtor: unlink from parent list (MUST be in there)
419  if (pParent)
420  {
421  Entry **ppCheck = &(pParent->pFirst);
422  while (*ppCheck != this)
423  {
424  ppCheck = &(*ppCheck)->pNext;
425  }
426  *ppCheck = pNext;
427  }
428 }
429 
430 
431 bool C4ScenarioListLoader::Entry::Load(C4Group *pFromGrp, const StdStrBuf *psFilename, bool fLoadEx)
432 {
433  // nothing to do if already loaded
434  if (fBaseLoaded && (fExLoaded || !fLoadEx)) return true;
435  C4Group Group;
436  // group specified: Load as child
437  if (pFromGrp)
438  {
439  assert(psFilename);
440  if (!Group.OpenAsChild(pFromGrp, psFilename->getData())) return false;
441  // set FN by complete entry name
442  this->sFilename.Take(Group.GetFullName());
443  }
444  else
445  {
446  // set FN by complete entry name
447  if (psFilename) this->sFilename.Copy(*psFilename);
448  // no parent group: Direct load from filename
449  if (!Group.Open(sFilename.getData())) return false;
450  }
451  // okay; load standard stuff from group
452  bool fNameLoaded=false, fIconLoaded=false;
453  if (fBaseLoaded)
454  {
455  fNameLoaded = fIconLoaded = true;
456  }
457  else
458  {
459  // Set default name as filename without extension
460  sName.Copy(GetFilename(sFilename.getData()));
461  char *szBuf = sName.GrabPointer();
462  RemoveExtension(szBuf);
463  sName.Take(szBuf);
464  // load entry specific stuff that's in the front of the group
465  if (!LoadCustomPre(Group))
466  return false;
467  // Load entry name
468  C4ComponentHost DefNames;
470  if (DefNames.GetLanguageString(Config.General.LanguageEx, sName))
471  fNameLoaded = true;
472  // load entry icon
473  if (Group.FindEntry(C4CFN_IconPNG) && fctIcon.Load(Group, C4CFN_IconPNG, C4FCT_Full, C4FCT_Full, false, 0))
474  fIconLoaded = true;
475  else
476  {
477  C4FacetSurface fctTemp;
478  if (Group.FindEntry(C4CFN_ScenarioIcon) && fctTemp.Load(Group, C4CFN_ScenarioIcon, C4FCT_Full, C4FCT_Full, true, 0))
479  {
480  // old style icon: Blit it on a pieace of paper
481  fctTemp.Surface->Lock();
482  for (int y=0; y<fctTemp.Hgt; ++y)
483  for (int x=0; x<fctTemp.Wdt; ++x)
484  {
485  uint32_t dwPix = fctTemp.Surface->GetPixDw(x,y, false);
486  // transparency has some tolerance...
487  if (Inside<uint8_t>(dwPix & 0xff, 0xb8, 0xff))
488  if (Inside<uint8_t>((dwPix>>0x08) & 0xff, 0x00, 0x0f))
489  if (Inside<uint8_t>((dwPix>>0x10) & 0xff, 0xb8, 0xff))
490  fctTemp.Surface->SetPixDw(x,y,0x00ffffff);
491  }
492  fctTemp.Surface->Unlock();
493  int iIconSize = C4Startup::Get()->Graphics.fctScenSelIcons.Hgt;
494  fctIcon.Create(iIconSize, iIconSize, C4FCT_Full, C4FCT_Full);
496  fctTemp.Draw(fctIcon.Surface, (fctIcon.Wdt-fctTemp.Wdt)/2, (fctIcon.Hgt-fctTemp.Hgt)/2);
497  fctTemp.Clear();
498  fIconLoaded = true;
499  }
500  }
501  // load any entryx-type-specific custom data (e.g. fallbacks for scenario title, and icon)
502  if (!LoadCustom(Group, fNameLoaded, fIconLoaded)) return false;
503  fBaseLoaded = true;
504  }
505  // load extended stuff: title picture
506  if (fLoadEx && !fExLoaded)
507  {
508  // load desc
509  C4ComponentHost DefDesc;
511  {
512  sDesc.Copy(DefDesc.GetData());
513  }
514  // load title
515  fctTitle.Load(Group, C4CFN_ScenarioTitle,C4FCT_Full,C4FCT_Full,false,true);
516  fExLoaded = true;
517  // load version
518  Group.LoadEntryString(C4CFN_Version, &sVersion);
519  }
520  // done, success
521  return true;
522 }
523 
524 // helper func: Recursive check whether a directory contains a .ocs or .ocf file
525 bool DirContainsScenarios(const char *szDir)
526 {
527  // create iterator on free store to avoid stack overflow with deeply recursed folders
528  DirectoryIterator *pIter = new DirectoryIterator(szDir);
529  const char *szChildFilename;
530  for (; (szChildFilename = **pIter); ++*pIter)
531  {
532  // Ignore directory navigation entries and CVS folders
533  if (C4Group_TestIgnore(szChildFilename)) continue;
534  if (WildcardMatch(C4CFN_ScenarioFiles, szChildFilename) || WildcardMatch(C4CFN_FolderFiles, szChildFilename)) break;
535  if (DirectoryExists(szChildFilename))
536  if (DirContainsScenarios(szChildFilename))
537  break;
538  }
539  delete pIter;
540  // return true if loop was broken, in which case a matching entry was found
541  return !!szChildFilename;
542 }
543 
545 {
546  // determine entry type by file type
547  const char *szFilename = sFilename.getData();
548  if (!szFilename || !*szFilename) return nullptr;
549  if (WildcardMatch(C4CFN_ScenarioFiles, sFilename.getData())) return new Scenario(pLoader, pParent);
550  if (WildcardMatch(C4CFN_FolderFiles, sFilename.getData())) return new SubFolder(pLoader, pParent);
551  // regular, open folder (C4Group-packed folders without extensions are not regarded, because they could contain anything!)
552  const char *szExt = GetExtension(szFilename);
553  if ((!szExt || !*szExt) && DirectoryExists(sFilename.getData()))
554  {
555  // open folders only if they contain a scenario or folder
556  if (DirContainsScenarios(szFilename))
557  return new RegularFolder(pLoader, pParent);
558  }
559  // type not recognized
560  return nullptr;
561 }
562 
563 bool C4ScenarioListLoader::Entry::RenameTo(const char *szNewName)
564 {
565  // change name+filename
566  // some name sanity validation
567  if (!szNewName || !*szNewName) return false;
568  if (SEqual(szNewName, sName.getData())) return true;
569  char fn[_MAX_PATH+1];
570  SCopy(szNewName, fn, _MAX_PATH);
571  // generate new file name
573  if (!*fn) return false;
574  const char *szExt = GetDefaultExtension();
575  if (szExt) { SAppend(".", fn, _MAX_PATH); SAppend(szExt, fn, _MAX_PATH); }
576  char fullfn[_MAX_PATH+1];
577  SCopy(sFilename.getData(), fullfn, _MAX_PATH);
578  char *fullfn_fn = GetFilename(fullfn);
579  SCopy(fn, fullfn_fn, _MAX_PATH - (fullfn_fn - fullfn));
580  StdCopyStrBuf strErr(LoadResStr("IDS_FAIL_RENAME"));
581  // check if a rename is due
582  if (!ItemIdentical(sFilename.getData(), fullfn))
583  {
584  // check for duplicate filename
585  if (ItemExists(fullfn))
586  {
587  StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_ERR_FILEEXISTS"), fullfn);
589  return false;
590  }
591  // OK; then rename
592  if (!C4Group_MoveItem(sFilename.getData(), fullfn, true))
593  {
594  StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_ERR_RENAMEFILE"), sFilename.getData(), fullfn);
596  return false;
597  }
598  sFilename.Copy(fullfn);
599  }
600  // update real name in group, if this is a group
601  if (C4Group_IsGroup(fullfn))
602  {
603  C4Group Grp;
604  if (!Grp.Open(fullfn))
605  {
606  StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_ERR_OPENFILE"), sFilename.getData(), Grp.GetError());
608  return false;
609  }
610  if (!Grp.Delete(C4CFN_Title))
611  {
612  StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_ERR_DELOLDTITLE"), sFilename.getData(), Grp.GetError());
614  return false;
615  }
616  if (!SetTitleInGroup(Grp, szNewName)) return false;
617  if (!Grp.Close())
618  {
619  StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_ERR_WRITENEWTITLE"), sFilename.getData(), Grp.GetError());
621  return false;
622  }
623  }
624  // update title
625  sName.Copy(szNewName);
626  // done
627  return true;
628 }
629 
630 bool C4ScenarioListLoader::Entry::SetTitleInGroup(C4Group &rGrp, const char *szNewTitle)
631 {
632  // default for group files: Create a title text file and set the title in there
633  // no title needed if filename is sufficient - except for scenarios, where a Scenario.txt could overwrite the title
634  if (!IsScenario())
635  {
636  StdStrBuf sNameByFile; sNameByFile.Copy(GetFilename(sFilename.getData()));
637  char *szBuf = sNameByFile.GrabPointer();
638  RemoveExtension(szBuf);
639  sNameByFile.Take(szBuf);
640  if (SEqual(szNewTitle, sNameByFile.getData())) return true;
641  }
642  // okay, make a title
643  StdStrBuf sTitle; sTitle.Format("%s:%s", Config.General.Language, szNewTitle);
644  if (!rGrp.Add(C4CFN_WriteTitle, sTitle, false, true))
645  {
646  StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_ERR_ERRORADDINGNEWTITLEFORFIL"), sFilename.getData(), rGrp.GetError());
648  return false;
649  }
650  return true;
651 }
652 
653 
654 // ------------------------------------
655 // Scenario
656 
658 {
659  // load scenario core first
660  StdStrBuf sFileContents;
661  if (!rGrp.LoadEntryString(C4CFN_ScenarioCore, &sFileContents)) return false;
662  if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(mkParAdapt(C4S, false), sFileContents, (rGrp.GetFullName() + DirSep C4CFN_ScenarioCore).getData()))
663  return false;
664  // Mission access
665  fNoMissionAccess = (!C4S.Head.MissionAccess.empty() && !SIsModule(Config.General.MissionAccess, C4S.Head.MissionAccess.c_str()));
666  // Localized parameter definitions. needed for achievements and parameter input boxes.
667  // Only show them for "real" scenarios
668  if (!C4S.Head.SaveGame && !C4S.Head.Replay)
669  {
670  // Skipping ahead in regular reading list, so keep other entries in memory
673  C4LangStringTable ScenarioLangStringTable;
675  ParameterDefs.Load(rGrp, &ScenarioLangStringTable);
676  // achievement images: Loaded from this entry and parent folder
677  nAchievements = 0;
678  const C4ScenarioParameterDefs *deflists[] = { pParent ? pParent->GetAchievementDefs() : nullptr, &ParameterDefs };
679  for (auto deflist : deflists)
680  {
681  if (!deflist) continue;
682  const C4ScenarioParameterDef *def; size_t idx=0;
683  while ((def = deflist->GetParameterDefByIndex(idx++)))
684  {
685  if (def->IsAchievement())
686  {
687  int32_t val = pLoader->GetAchievements().GetValueByID(C4ScenarioParameters::AddFilename2ID(rGrp.GetFullName().getData(), def->GetID()).getData(), def->GetDefault());
688  if (val)
689  {
690  // player has this achievement - find graphics for it
691  const char *achievement_gfx = def->GetAchievement();
692  StdStrBuf sAchievementFilename(C4CFN_Achievements);
693  sAchievementFilename.Replace("*", achievement_gfx);
694  // look in scenario
695  if (!fctAchievements[nAchievements].Load(rGrp, sAchievementFilename.getData(), C4FCT_Height, C4FCT_Full, false, true))
696  {
697  // look in parent folder
698  const C4FacetSurface *fct = nullptr;
699  const C4AchievementGraphics *parent_achv_gfx;
700  if (pParent && (parent_achv_gfx = pParent->GetAchievementGfx())) fct = parent_achv_gfx->FindByName(achievement_gfx);
701  // look in main gfx group file
702  if (!fct) fct = ::GraphicsResource.Achievements.FindByName(achievement_gfx);
703  if (!fct) continue; // achievement graphics not found :(
704  fctAchievements[nAchievements].Set((const C4Facet &)*fct);
705  }
706  // section by achievement index (1-based, since zero means no achievement)
707  if (val>1) fctAchievements[nAchievements].X += fctAchievements[nAchievements].Wdt * (val-1);
708  // description for this achievement is taken from option
709  const C4ScenarioParameterDef::Option *opt = def->GetOptionByValue(val);
710  if (opt) sAchievementDescriptions[nAchievements] = opt->Description;
711  // keep track of achievement count
712  ++nAchievements;
713  if (nAchievements == C4StartupScenSel_MaxAchievements) break;
714  }
715  }
716  }
717  }
718  }
719  return true;
720 }
721 
722 bool C4ScenarioListLoader::Scenario::LoadCustom(C4Group &rGrp, bool fNameLoaded, bool fIconLoaded)
723 {
724  // icon fallback: Standard scenario icon
725  if (!fIconLoaded)
726  {
727  iIconIndex = C4S.Head.Icon;
728  fctIcon.Set(C4Startup::Get()->Graphics.fctScenSelIcons.GetSection(C4S.Head.Icon));
729  }
730  // scenario name fallback to core
731  if (!fNameLoaded)
732  sName = C4S.Head.Title;
733  // difficulty: Set only for regular rounds (not savegame or record) to avoid bogus sorting
734  if (!C4S.Head.SaveGame && !C4S.Head.Replay)
735  iDifficulty = C4S.Head.Difficulty;
736  else
737  iDifficulty = 0;
738  // minimum required player count
739  iMinPlrCount = C4S.GetMinPlayer();
740  return true;
741 }
742 
743 bool C4ScenarioListLoader::Scenario::GetAchievement(int32_t idx, C4Facet *out_facet, const char **out_description)
744 {
745  // return true and fill output parameters if player got the indexed achievement
746  if (idx < 0 || idx >= nAchievements) return false;
747  *out_facet = fctAchievements[idx];
748  *out_description = sAchievementDescriptions[idx].getData();
749  return true;
750 }
751 
753 {
754  // gogo!
755  if (!(C4StartupScenSelDlg::pInstance)) return false;
756  return (C4StartupScenSelDlg::pInstance)->StartScenario(this);
757 }
758 
760 {
761  // safety
763  if (!pDlg) return false;
764  // check mission access
765  if (!HasMissionAccess())
766  {
767  sErrOut.Copy(LoadResStr("IDS_PRC_NOMISSIONACCESS"));
768  return false;
769  }
770  // replay
771  if (C4S.Head.Replay)
772  {
773  // replays can currently not be launched in network mode
774  if (pDlg->IsNetworkStart())
775  {
776  sErrOut.Copy(LoadResStr("IDS_PRC_NONETREPLAY"));
777  return false;
778  }
779  }
780  // regular game
781  else
782  {
783  // check player count
784  int32_t iPlrCount = SModuleCount(Config.General.Participants);
785  int32_t iMaxPlrCount = C4S.Head.MaxPlayer;
786  if (C4S.Head.SaveGame)
787  {
788  // Some scenarios have adjusted MaxPlayerCount to 0 after starting to prevent future joins
789  // make sure it's possible to start the savegame anyway
790  iMaxPlrCount = std::max<int32_t>(iMinPlrCount, iMaxPlrCount);
791  }
792  // normal scenarios: At least one player except in network mode, where it is possible to wait for the additional players
793  // Melees need at least two
794  if ((iPlrCount < iMinPlrCount))
795  {
796  if (pDlg->IsNetworkStart())
797  {
798  // network game: Players may yet join in lobby
799  // only issue a warning for too few players (by setting the error but not returning false here)
800  sErrOut.Format(LoadResStr("IDS_MSG_TOOFEWPLAYERSNET"), (int) iMinPlrCount);
801  }
802  else
803  {
804  // for regular games, this is a fatal no-start-cause
805  sErrOut.Format(LoadResStr("IDS_MSG_TOOFEWPLAYERS"), (int) iMinPlrCount);
806  return false;
807  }
808  }
809  // scenarios (both normal and savegame) may also impose a maximum player restriction
810  if (iPlrCount > iMaxPlrCount)
811  {
812  sErrOut.Format(LoadResStr("IDS_MSG_TOOMANYPLAYERS"), (int) C4S.Head.MaxPlayer);
813  return false;
814  }
815  }
816  // Okay, start!
817  return true;
818 }
819 
821 {
822  return StdCopyStrBuf(LoadResStr("IDS_BTN_STARTGAME"));
823 }
824 
826 {
827  return StdCopyStrBuf(LoadResStr("IDS_DLGTIP_SCENSELNEXT"));
828 }
829 
830 
831 // ------------------------------------
832 // Folder
833 
835 {
836  if (pMapData) delete pMapData;
837  ClearChildren();
838 }
839 
841 {
842  // open as subfolder
843  if (!C4StartupScenSelDlg::pInstance) return false;
844  return C4StartupScenSelDlg::pInstance->OpenFolder(this);
845 }
846 int
847 #ifdef _MSC_VER
848 __cdecl
849 #endif
850 EntrySortFunc(const void *pEl1, const void *pEl2)
851 {
852  C4ScenarioListLoader::Entry *pEntry1 = *(C4ScenarioListLoader::Entry * const *) pEl1, *pEntry2 = *(C4ScenarioListLoader::Entry * const *) pEl2;
853  // sort folders before scenarios
854  bool fS1 = !pEntry1->GetIsFolder(), fS2 = !pEntry2->GetIsFolder();
855  if (fS1 != fS2) return fS1-fS2;
856  // sort by folder index (undefined index 0 goes to the end)
857  if (!Config.Startup.AlphabeticalSorting) if (pEntry1->GetFolderIndex() || pEntry2->GetFolderIndex())
858  {
859  if (!pEntry1->GetFolderIndex()) return +1;
860  if (!pEntry2->GetFolderIndex()) return -1;
861  int32_t iDiff = pEntry1->GetFolderIndex() - pEntry2->GetFolderIndex();
862  if (iDiff) return iDiff;
863  }
864  // sort by numbered standard scenario icons
865  if (Inside(pEntry1->GetIconIndex(), 2, 11))
866  {
867  int32_t iDiff = pEntry1->GetIconIndex() - pEntry2->GetIconIndex();
868  if (iDiff) return iDiff;
869  }
870  // sort by difficulty (undefined difficulty goes to the end)
871  if (!Config.Startup.AlphabeticalSorting) if (pEntry1->GetDifficulty() || pEntry2->GetDifficulty())
872  {
873  if (!pEntry1->GetDifficulty()) return +1;
874  if (!pEntry2->GetDifficulty()) return -1;
875  int32_t iDiff = pEntry1->GetDifficulty() - pEntry2->GetDifficulty();
876  if (iDiff) return iDiff;
877  }
878  // otherwise, sort by name
879  return stricmp(pEntry1->GetName().getData(), pEntry2->GetName().getData());
880 }
881 
883 {
884  uint32_t iCount = 0;
885  for (Entry *i = pFirst; i; i = i->pNext) ++iCount;
886  return iCount;
887 }
888 
890 {
891  // use C-Library-QSort on a buffer of entry pointers; then re-link list
892  if (!pFirst) return;
893  uint32_t iCount,i;
894  Entry **ppEntries = new Entry *[i = iCount = GetEntryCount()], **ppI, *pI=pFirst, **ppIThis;
895  for (ppI = ppEntries; i--; pI = pI->pNext) *ppI++ = pI;
896  qsort(ppEntries, iCount, sizeof(Entry *), &EntrySortFunc);
897  ppIThis = &pFirst;
898  for (ppI = ppEntries; iCount--; ppIThis = &((*ppIThis)->pNext)) *ppIThis = *ppI++;
899  *ppIThis = nullptr;
900  delete [] ppEntries;
901 }
902 
904 {
905  // folder deletion: del all the tree non-recursively
906  Folder *pDelFolder = this, *pCheckFolder;
907  for (;;)
908  {
909  // delete all children as long as they are not folders
910  Entry *pChild;
911  while ((pChild = pDelFolder->pFirst))
912  if ((pCheckFolder = pChild->GetIsFolder()))
913  // child entry if folder: Continue delete in there
914  pDelFolder = pCheckFolder;
915  else
916  // regular child entry: del it
917  // destructor of child will remove it from list
918  delete pChild;
919  // this emptied: Done!
920  if (pDelFolder == this) break;
921  // deepest child recursion reached: Travel up folders
922  pDelFolder = (pCheckFolder = pDelFolder)->pParent;
923  assert(pDelFolder);
924  delete pCheckFolder;
925  }
926 }
927 
928 bool C4ScenarioListLoader::Folder::LoadContents(C4ScenarioListLoader *pLoader, C4Group *pFromGrp, const StdStrBuf *psFilename, bool fLoadEx, bool fReload)
929 {
930  // contents already loaded?
931  if (fContentsLoaded && !fReload) return true;
932  // clear previous
933  if (pMapData) { delete pMapData; pMapData = nullptr; }
934  // if filename is not given, assume it's been loaded in this entry
935  if (!psFilename) psFilename = &this->sFilename; else this->sFilename = *psFilename;
936  // nothing loaded: Load now
937  if (!DoLoadContents(pLoader, pFromGrp, *psFilename, fLoadEx)) return false;
938  // sort loaded stuff by name
939  Sort();
940  return true;
941 }
942 
944 {
945  // do a case-insensitive filename comparison
946  for (Entry *pEntry = pFirst; pEntry; pEntry = pEntry->GetNext())
947  if (SEqualNoCase(szFilename, GetFilename(pEntry->GetEntryFilename().getData())))
948  return pEntry;
949  // nothing found
950  return nullptr;
951 }
952 
954 {
955  return StdCopyStrBuf(LoadResStr("IDS_BTN_OPEN"));
956 }
957 
959 {
960  return StdCopyStrBuf(LoadResStr("IDS_DLGTIP_SCENSELNEXT"));
961 }
962 
964 {
965  return false;
966 }
967 
969 {
970  // load folder core if available
971  StdStrBuf sFileContents;
972  if (rGrp.LoadEntryString(C4CFN_FolderCore, &sFileContents))
973  if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(C4F, sFileContents, (rGrp.GetFullName() + DirSep C4CFN_FolderCore).getData()))
974  return false;
975  return true;
976 }
977 
978 // ------------------------------------
979 // SubFolder
980 
981 bool C4ScenarioListLoader::SubFolder::LoadCustom(C4Group &rGrp, bool fNameLoaded, bool fIconLoaded)
982 {
983  // default icon fallback
984  if (!fIconLoaded)
985  {
987  else iIconIndex = C4StartupScenSel_DefaultIcon_Folder;
988  fctIcon.Set(C4Startup::Get()->Graphics.fctScenSelIcons.GetSection(iIconIndex));
989  }
990  // folder index
991  iFolderIndex = C4F.Head.Index;
992  return true;
993 }
994 
995 bool C4ScenarioListLoader::SubFolder::DoLoadContents(C4ScenarioListLoader *pLoader, C4Group *pFromGrp, const StdStrBuf &sFilename, bool fLoadEx)
996 {
997  assert(pLoader);
998  // clear any previous
999  ClearChildren();
1000  // group specified: Load as child
1001  C4Group Group;
1002  if (pFromGrp)
1003  {
1004  if (!Group.OpenAsChild(pFromGrp, sFilename.getData())) return false;
1005  }
1006  else
1007  // no parent group: Direct load from filename
1008  if (!Group.Open(sFilename.getData())) return false;
1009  // Load achievement data contained scenarios can fall back to
1010  C4LangStringTable FolderLangStringTable;
1012  AchievementDefs.Load(Group, &FolderLangStringTable);
1013  AchievementGfx.Init(Group);
1014  // get number of entries, to estimate progress
1015  const char *szC4CFN_ScenarioFiles = C4CFN_ScenarioFiles; // assign values for constant comparison
1016  const char *szSearchMask; int32_t iEntryCount=0;
1017  for (szSearchMask = szC4CFN_ScenarioFiles; szSearchMask;)
1018  {
1019  Group.ResetSearch();
1020  while (Group.FindNextEntry(szSearchMask)) ++iEntryCount;
1021  // next search mask
1022  if (szSearchMask == szC4CFN_ScenarioFiles)
1023  szSearchMask = C4CFN_FolderFiles;
1024  else
1025  szSearchMask = nullptr;
1026  }
1027  // initial progress estimate
1028  if (!pLoader->DoProcessCallback(0, iEntryCount)) return false;
1029  // iterate through group contents
1030  char ChildFilename[_MAX_FNAME+1]; StdStrBuf sChildFilename; int32_t iLoadCount=0;
1031  for (szSearchMask = szC4CFN_ScenarioFiles; szSearchMask;)
1032  {
1033  Group.ResetSearch();
1034  while (Group.FindNextEntry(szSearchMask, ChildFilename))
1035  {
1036  sChildFilename.Ref(ChildFilename);
1037  // okay; create this item
1038  Entry *pNewEntry = Entry::CreateEntryForFile(sChildFilename, pLoader, this);
1039  if (pNewEntry)
1040  {
1041  // ...and load it
1042  if (!pNewEntry->Load(&Group, &sChildFilename, fLoadEx))
1043  {
1044  DebugLogF(R"(Error loading entry "%s" in SubFolder "%s"!)", sChildFilename.getData(), Group.GetFullName().getData());
1045  delete pNewEntry;
1046  }
1047  }
1048  // mark progress
1049  if (!pLoader->DoProcessCallback(++iLoadCount, iEntryCount)) return false;
1050  }
1051  // next search mask
1052  if (szSearchMask == szC4CFN_ScenarioFiles)
1053  szSearchMask = C4CFN_FolderFiles;
1054  else
1055  szSearchMask = nullptr;
1056  }
1057  // load map folder data
1058  if (Group.FindEntry(C4CFN_MapFolderData))
1059  {
1060  pMapData = new C4MapFolderData();
1061  if (!pMapData->Load(Group, this))
1062  {
1063  // load error :(
1064  delete pMapData;
1065  pMapData = nullptr;
1066  }
1067  }
1068  // done, success
1069  fContentsLoaded = true;
1070  return true;
1071 }
1072 
1073 
1074 // ------------------------------------
1075 // RegularFolder
1076 
1078 
1079 bool C4ScenarioListLoader::RegularFolder::LoadCustom(C4Group &rGrp, bool fNameLoaded, bool fIconLoaded)
1080 {
1081  // default icon fallback
1082  if (!fIconLoaded)
1083  fctIcon.Set(C4Startup::Get()->Graphics.fctScenSelIcons.GetSection(C4StartupScenSel_DefaultIcon_WinFolder));
1084  // folder index
1085  iFolderIndex = C4F.Head.Index;
1086  return true;
1087 }
1088 
1089 bool C4ScenarioListLoader::RegularFolder::DoLoadContents(C4ScenarioListLoader *pLoader, C4Group *pFromGrp, const StdStrBuf &sFilename, bool fLoadEx)
1090 {
1091  // clear any previous
1092  ClearChildren();
1093  // regular folders must exist and not be within group!
1094  assert(!pFromGrp);
1095  if (sFilename.getData() && sFilename[0])
1096  Merge(sFilename.getData());
1097 
1098  // get number of entries, to estimate progress
1099  int32_t iCountLoaded=0, iCountTotal=0;
1100  NameList::iterator it;
1101  for (it = contents.begin(); it != contents.end(); ++it)
1102  {
1103  if (!DirectoryExists(it->c_str())) continue;
1104  DirectoryIterator DirIter(it->c_str());
1105  const char *szChildFilename;
1106  for (; (szChildFilename = *DirIter); ++DirIter)
1107  {
1108  if (!*szChildFilename || *GetFilename(szChildFilename)=='.') continue;
1109  ++iCountTotal;
1110  }
1111  }
1112  // initial progress estimate
1113  if (!pLoader->DoProcessCallback(iCountLoaded, iCountTotal)) return false;
1114 
1115  // do actual loading of files
1116  std::set<std::string> names;
1117  const char *szChildFilename;
1118  for (it = contents.begin(); it != contents.end(); ++it)
1119  {
1120  for (DirectoryIterator DirIter(it->c_str()); (szChildFilename = *DirIter); ++DirIter)
1121  {
1122  StdStrBuf sChildFilename(szChildFilename);
1123  szChildFilename = GetFilename(szChildFilename);
1124 
1125  // Ignore directory navigation entries and CVS folders
1126  if (C4Group_TestIgnore(szChildFilename)) continue;
1127  if (names.find(szChildFilename) != names.end()) continue;
1128  names.insert(szChildFilename);
1129  // filename okay; create this item
1130  Entry *pNewEntry = Entry::CreateEntryForFile(sChildFilename, pLoader, this);
1131  if (pNewEntry)
1132  {
1133  // ...and load it
1134  if (!pNewEntry->Load(nullptr, &sChildFilename, fLoadEx))
1135  {
1136  DebugLogF(R"(Error loading entry "%s" in Folder "%s"!)", szChildFilename, it->c_str());
1137  delete pNewEntry;
1138  }
1139  }
1140  // progress callback
1141  if (!pLoader->DoProcessCallback(++iCountLoaded, iCountTotal)) return false;
1142  }
1143  }
1144  // done, success
1145  fContentsLoaded = true;
1146  return true;
1147 }
1148 
1150 {
1151  contents.emplace_back(szPath);
1152 }
1153 
1154 // ------------------------------------
1155 // C4ScenarioListLoader
1156 
1157 C4ScenarioListLoader::C4ScenarioListLoader(const C4ScenarioParameters &Achievements) : Achievements(Achievements), pRootFolder(nullptr), pCurrFolder(nullptr),
1158  iLoading(0), iProgress(0), iMaxProgress(0), fAbortThis(false), fAbortPrevious(false)
1159 {
1160 }
1161 
1163 {
1164  if (pRootFolder) delete pRootFolder;
1165 }
1166 
1167 bool C4ScenarioListLoader::BeginActivity(bool fAbortPrevious)
1168 {
1169  // if previous activities were running, stop them first if desired
1170  if (iLoading && fAbortPrevious)
1171  this->fAbortPrevious = true;
1172  // mark this activity
1173  ++iLoading;
1174  // progress of activity not yet decided
1175  iProgress = iMaxProgress = 0;
1176  // okay; start activity
1177  return true;
1178 }
1179 
1180 void C4ScenarioListLoader::EndActivity()
1181 {
1182  assert(iLoading);
1183  if (!--iLoading)
1184  {
1185  // last activity done: Reset any flags
1186  fAbortThis = false;
1187  fAbortPrevious = false;
1188  iProgress = iMaxProgress = 0;
1189  }
1190  else
1191  {
1192  // child activity done: Transfer abort flag for next activity
1193  fAbortThis = fAbortPrevious;
1194  }
1195 }
1196 
1197 bool C4ScenarioListLoader::DoProcessCallback(int32_t iProgress, int32_t iMaxProgress)
1198 {
1199  this->iProgress = iProgress;
1200  this->iMaxProgress = iMaxProgress;
1201  // callback to dialog
1202  if (C4StartupScenSelDlg::pInstance) C4StartupScenSelDlg::pInstance->ProcessCallback();
1203  // process callback - abort at a few ugly circumstances...
1204  if (!Application.ScheduleProcs() // WM_QUIT message?
1205  || !C4StartupScenSelDlg::pInstance // host dialog removed?
1206  || !C4StartupScenSelDlg::pInstance->IsShown() // host dialog closed?
1207  ) return false;
1208  // and also abort if flagged
1209  return !fAbortThis;
1210 }
1211 
1212 bool C4ScenarioListLoader::Load(const StdStrBuf &sRootFolder)
1213 {
1214  // (unthreaded) loading of all entries in root folder
1215  if (!BeginActivity(true)) return false;
1216  if (pRootFolder) { delete pRootFolder; pRootFolder = nullptr; }
1217  pCurrFolder = pRootFolder = new RegularFolder(this, nullptr);
1218  // Load regular game data if no explicit path specified
1219  if(!sRootFolder.getData())
1220  for(const auto & iter : Reloc)
1221  pRootFolder->Merge(iter.strBuf.getData());
1222  bool fSuccess = pRootFolder->LoadContents(this, nullptr, &sRootFolder, false, false);
1223  EndActivity();
1224  return fSuccess;
1225 }
1226 
1227 bool C4ScenarioListLoader::Load(Folder *pSpecifiedFolder, bool fReload)
1228 {
1229  // call safety
1230  if (!pRootFolder || !pSpecifiedFolder) return false;
1231  // set new current and load it
1232  if (!BeginActivity(true)) return false;
1233  pCurrFolder = pSpecifiedFolder;
1234  bool fSuccess = pCurrFolder->LoadContents(this, nullptr, nullptr, false, fReload);
1235  EndActivity();
1236  return fSuccess;
1237 }
1238 
1240 {
1241  // call safety
1242  if (!pRootFolder || !pEntry) return false;
1243  // load info of selection
1244  if (!BeginActivity(false)) return false;
1245  bool fSuccess = pEntry->Load(nullptr, nullptr, true);
1246  EndActivity();
1247  return fSuccess;
1248 }
1249 
1251 {
1252  // call safety
1253  if (!pRootFolder || !pCurrFolder) return false;
1254  // already in root: Can't go up
1255  if (pCurrFolder == pRootFolder) return false;
1256  // otherwise, up one level
1257  return Load(pCurrFolder->GetParent(), false);
1258 }
1259 
1261 {
1262  // call safety
1263  if (!pRootFolder || !pCurrFolder) return false;
1264  // reload current
1265  return Load(pCurrFolder, true);
1266 }
1267 
1268 
1269 
1270 
1271 
1272 
1273 // ----------------------------------------------------------------
1274 // Scenario selection GUI
1275 
1276 
1277 // font clrs
1278 const uint32_t ClrScenarioItem = 0xff000000,
1279  ClrScenarioItemXtra = 0x7f000000,
1281 
1282 // ------------------------------------------------
1283 // --- C4StartupScenSelDlg::ScenListItem
1285  : pIcon(nullptr), pNameLabel(nullptr), pScenListEntry(pForEntry)
1286 {
1287  assert(pScenListEntry);
1288  CStdFont &rUseFont = C4Startup::Get()->Graphics.BookFont;
1289  StdStrBuf sIgnore;
1290  bool fEnabled = pScenListEntry->CanOpen(sIgnore) && !pScenListEntry->IsGrayed();
1291  // calc height
1292  int32_t iHeight = rUseFont.GetLineHeight() + 2 * IconLabelSpacing;
1293  // create subcomponents
1294  pIcon = new C4GUI::Picture(C4Rect(0, 0, iHeight, iHeight), true);
1295  pIcon->SetFacet(pScenListEntry->GetIconFacet());
1296  pNameLabel = new C4GUI::Label(pScenListEntry->GetName().getData(), iHeight + IconLabelSpacing, IconLabelSpacing, ALeft, fEnabled ? ClrScenarioItem : ClrScenarioItemDisabled, &rUseFont, false, false);
1297  // achievement components
1298  for (int32_t i=0; i<C4StartupScenSel_MaxAchievements; ++i)
1299  {
1300  C4Facet fct; const char *desc;
1301  if (pForEntry->GetAchievement(i, &fct, &desc))
1302  {
1303  ppAchievements[i] = new C4GUI::Picture(C4Rect(iHeight * (i+2), 0, iHeight, iHeight), true); // position will be adjusted later
1304  ppAchievements[i]->SetFacet(fct);
1305  ppAchievements[i]->SetToolTip(desc);
1306  }
1307  else
1308  {
1309  ppAchievements[i] = nullptr;
1310  }
1311  }
1312  // calc own bounds - use icon bounds only, because only the height is used when the item is added
1313  SetBounds(pIcon->GetBounds());
1314  // add components
1315  AddElement(pIcon); AddElement(pNameLabel);
1316  for (auto & ppAchievement : ppAchievements) if (ppAchievement) AddElement(ppAchievement);
1317  // tooltip by name, so long names can be read via tooltip
1318  SetToolTip(pScenListEntry->GetName().getData());
1319  // add to listbox (will get resized horizontally and moved) - zero indent; no tree structure in this dialog
1320  pForListBox->InsertElement(this, pInsertBeforeElement, 0);
1321  // update name label width to reflect new horizontal size
1322  // name label width must be set so rename edit will take its size
1323  pNameLabel->SetAutosize(false);
1324  C4Rect rcNLB = pNameLabel->GetBounds(); rcNLB.Wdt = GetClientRect().Wdt - rcNLB.x - IconLabelSpacing;
1325  pNameLabel->SetBounds(rcNLB);
1326 }
1327 
1329 {
1330  // parent for client rect
1331  typedef C4GUI::Window ParentClass;
1332  ParentClass::UpdateOwnPos();
1333  // reposition achievement items
1334  C4GUI::ComponentAligner caBounds(GetContainedClientRect(), IconLabelSpacing, IconLabelSpacing);
1335  for (auto & ppAchievement : ppAchievements) if (ppAchievement)
1336  {
1337  ppAchievement->SetBounds(caBounds.GetFromRight(caBounds.GetHeight()));
1338  }
1339 }
1340 
1341 void C4StartupScenSelDlg::ScenListItem::MouseInput(C4GUI::CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam)
1342 {
1343  // double-click opens/starts item - currently processed by ListBox already!
1344  // inherited processing
1345  typedef C4GUI::Window BaseClass;
1346  BaseClass::MouseInput(rMouse, iButton, iX, iY, dwKeyParam);
1347 }
1348 
1350 {
1351  // return whether this item can be selected by entering given char:
1352  // first char of name must match
1353  // FIXME: make unicode-ready
1354  if (!pScenListEntry) return false;
1355  const char *szName = pScenListEntry->GetName().getData();
1356  return szName && (toupper(*szName) == toupper(c[0]));
1357 }
1358 
1360 {
1361  // rename this entry
1363  return true;
1364 }
1365 
1367 {
1368  // no renaming
1369  C4StartupScenSelDlg::pInstance->SetRenamingDone();
1370 }
1371 
1373 {
1374  // check validity for new name
1375  if (!GetEntry()->RenameTo(szNewName)) return C4GUI::RenameEdit::RR_Invalid;
1376  // rename label
1377  pNameLabel->SetText(GetEntry()->GetName().getData());
1378  // main dlg update
1379  C4StartupScenSelDlg::pInstance->SetRenamingDone();
1380  C4StartupScenSelDlg::pInstance->ResortFolder();
1381  C4StartupScenSelDlg::pInstance->UpdateSelection();
1382  C4StartupScenSelDlg::pInstance->FocusScenList();
1383  // done; rename accepted and control deleted by ResortFolder
1385 }
1386 
1387 // ------------------------------------------------
1388 // --- C4StartupScenSelDlg
1389 
1390 C4StartupScenSelDlg::C4StartupScenSelDlg(bool fNetwork) : C4StartupDlg(LoadResStrNoAmp(fNetwork ? "IDS_DLG_NETSTART" : "IDS_DLG_STARTGAME")), pScenLoader(nullptr), pMapData(nullptr), pfctBackground(nullptr), fIsInitialLoading(false), fStartNetworkGame(fNetwork), pRenameEdit(nullptr)
1391 {
1392  // ctor
1393  // assign singleton
1394  pInstance = this;
1395 
1396  // screen calculations
1397  UpdateSize();
1398  int32_t iButtonWidth,iCaptionFontHgt;
1399  int iButtonHeight = C4GUI_ButtonHgt;
1400  int iBookPageWidth;
1401  int iExtraHPadding = rcBounds.Wdt >= 700 ? rcBounds.Wdt/50 : 0;
1402  ::GraphicsResource.CaptionFont.GetTextExtent("<< BACK", iButtonWidth, iCaptionFontHgt, true);
1403  iButtonWidth *= 3;
1404  C4GUI::ComponentAligner caMain(GetClientRect(), 0,0, true);
1405  C4GUI::ComponentAligner caButtonArea(caMain.GetFromBottom(caMain.GetHeight()/8),rcBounds.Wdt/(rcBounds.Wdt >= 700 ? 128 : 256),0);
1406  C4Rect rcMap = caMain.GetCentered(caMain.GetWidth(), caMain.GetHeight());
1407 
1408  // tabular for different scenario selection designs
1409  pScenSelStyleTabular = new C4GUI::Tabular(rcMap, C4GUI::Tabular::tbNone);
1410  pScenSelStyleTabular->SetSheetMargin(0);
1411  pScenSelStyleTabular->SetGfx(&C4Startup::Get()->Graphics.fctDlgPaper, &C4Startup::Get()->Graphics.fctOptionsTabClip, &C4Startup::Get()->Graphics.fctOptionsIcons, &C4Startup::Get()->Graphics.BookSmallFont, false);
1412  AddElement(pScenSelStyleTabular);
1413  C4GUI::Tabular::Sheet *pSheetBook = pScenSelStyleTabular->AddSheet(nullptr);
1414  /* C4GUI::Tabular::Sheet *pSheetMap = */ pScenSelStyleTabular->AddSheet(nullptr);
1415 
1416  // scenario selection list
1417  C4GUI::ComponentAligner caBook(pSheetBook->GetClientRect(), caMain.GetWidth()/20, caMain.GetHeight()/20, true);
1418  C4GUI::ComponentAligner caBookLeft(caBook.GetFromLeft(iBookPageWidth=caBook.GetWidth()*4/9+4-iExtraHPadding*2), 0,5);
1419 
1420  CStdFont &rScenSelCaptionFont = C4Startup::Get()->Graphics.BookFontTitle;
1421  pScenSelCaption = new C4GUI::Label("", caBookLeft.GetFromTop(rScenSelCaptionFont.GetLineHeight()), ACenter, ClrScenarioItem, &rScenSelCaptionFont, false);
1422  pSheetBook->AddElement(pScenSelCaption);
1423  pScenSelCaption->SetToolTip(LoadResStr("IDS_DLGTIP_SELECTSCENARIO"));
1424  pScenSelList = new C4GUI::ListBox(caBookLeft.GetAll());
1425  pScenSelList->SetToolTip(LoadResStr("IDS_DLGTIP_SELECTSCENARIO"));
1426  pScenSelList->SetDecoration(false, &C4Startup::Get()->Graphics.sfctBookScroll, true);
1427  pSheetBook->AddElement(pScenSelList);
1430  // scenario selection list progress label
1431  pScenSelProgressLabel = new C4GUI::Label("", pScenSelList->GetBounds().GetMiddleX(), pScenSelList->GetBounds().GetMiddleX()-iCaptionFontHgt/2, ACenter, ClrScenarioItem, &(C4Startup::Get()->Graphics.BookFontCapt), false);
1432  pSheetBook->AddElement(pScenSelProgressLabel);
1433 
1434  // right side of book: Displaying current selection
1435  C4Rect bounds = caBook.GetFromRight(iBookPageWidth);
1436  const int32_t AvailWidth = bounds.Wdt;
1437  const int32_t AvailHeight = 2 * bounds.Hgt / 5;
1438  int32_t PictureWidth, PictureHeight;
1440  {
1441  PictureWidth = C4StartupScenSel_TitlePictureWdt * AvailWidth / C4StartupScenSel_TitlePictureWdt;
1443  }
1444  else
1445  {
1446  PictureWidth = C4StartupScenSel_TitlePictureWdt * AvailHeight / C4StartupScenSel_TitlePictureHgt;
1448  }
1449  pSelectionInfo = new C4GUI::TextWindow(bounds, PictureWidth+2*C4StartupScenSel_TitleOverlayMargin, PictureHeight+2*C4StartupScenSel_TitleOverlayMargin,
1450  C4StartupScenSel_TitlePicturePadding, 100, 4096, nullptr, true, &C4Startup::Get()->Graphics.fctScenSelTitleOverlay, C4StartupScenSel_TitleOverlayMargin);
1451  pSelectionInfo->SetDecoration(false, false, &C4Startup::Get()->Graphics.sfctBookScroll, true);
1452  pSheetBook->AddElement(pSelectionInfo);
1453 
1454  // bottom of right side of book: Custom options on selection
1455  // Arbitrary height and invisible by default. Height will be adjusted when options are created.
1456  pSelectionOptions = new C4GameOptionsList(C4Rect(bounds.x, bounds.y+bounds.Hgt-10, bounds.Wdt, 10), false, fNetwork ? C4GameOptionsList::GOLS_PreGameNetwork : C4GameOptionsList::GOLS_PreGameSingle);
1457  pSelectionOptions->SetDecoration(false, &C4Startup::Get()->Graphics.sfctBookScroll, true);
1458  pSelectionOptions->SetVisibility(false);
1459  pSheetBook->AddElement(pSelectionOptions);
1460 
1461  // back button
1463  AddElement(btn = new C4GUI::CallbackButton<C4StartupScenSelDlg>(LoadResStr("IDS_BTN_BACK"), caButtonArea.GetFromLeft(iButtonWidth, iButtonHeight), &C4StartupScenSelDlg::OnBackBtn));
1464  btn->SetToolTip(LoadResStr("IDS_DLGTIP_BACKMAIN"));
1465  AddElement(btn);
1466  // next button
1467  pOpenBtn = new C4GUI::CallbackButton<C4StartupScenSelDlg>(LoadResStr("IDS_BTN_OPEN"), caButtonArea.GetFromRight(iButtonWidth, iButtonHeight), &C4StartupScenSelDlg::OnNextBtn);
1468  pOpenBtn->SetToolTip(LoadResStr("IDS_DLGTIP_SCENSELNEXT"));
1469  // game options boxes
1470  pGameOptionButtons = new C4GameOptionButtons(caButtonArea.GetAll(), fNetwork, true, false);
1471  AddElement(pGameOptionButtons);
1472  // next button adding
1473  AddElement(pOpenBtn);
1474 
1475  // dlg starts with focus on listbox
1476  SetFocus(pScenSelList, false);
1477 
1478  // key bindings
1479  C4CustomKey::CodeList keys;
1480  keys.emplace_back(K_BACK); keys.emplace_back(K_LEFT);
1481  pKeyBack = new C4KeyBinding(keys, "StartupScenSelFolderUp", KEYSCOPE_Gui,
1483  pKeyRefresh = new C4KeyBinding(C4KeyCodeEx(K_F5), "StartupScenSelReload", KEYSCOPE_Gui,
1485  pKeyForward = new C4KeyBinding(C4KeyCodeEx(K_RIGHT), "StartupScenSelNext", KEYSCOPE_Gui,
1487  pKeyRename = new C4KeyBinding(C4KeyCodeEx(K_F2), "StartupScenSelRename", KEYSCOPE_Gui,
1489  pKeyDelete = new C4KeyBinding(C4KeyCodeEx(K_DELETE), "StartupScenSelDelete", KEYSCOPE_Gui,
1491  pKeyCheat = new C4KeyBinding(C4KeyCodeEx(K_M, KEYS_Control), "StartupScenSelCheat", KEYSCOPE_Gui,
1493 }
1494 
1496 {
1497  if (pScenLoader) delete pScenLoader;
1498  if (this == pInstance) pInstance = nullptr;
1499  delete pKeyCheat;
1500  delete pKeyDelete;
1501  delete pKeyRename;
1502  delete pKeyForward;
1503  delete pKeyRefresh;
1504  delete pKeyBack;
1505 }
1506 
1508 {
1509  // draw background
1510  if (pfctBackground)
1511  DrawBackground(cgo, *pfctBackground);
1512 }
1513 
1515 {
1517  // Collect achievements of all activated players
1518  UpdateAchievements();
1519  // init file list
1520  fIsInitialLoading = true;
1521  if (!pScenLoader) pScenLoader = new C4ScenarioListLoader(Achievements);
1522  pScenLoader->Load(StdStrBuf());
1523  UpdateList();
1524  UpdateSelection();
1525  fIsInitialLoading = false;
1526  // network activation by dialog type
1527  Game.NetworkActive = fStartNetworkGame;
1528 }
1529 
1531 {
1532  AbortRenaming();
1533  // clear laoded scenarios
1534  if (pScenLoader)
1535  {
1536  delete pScenLoader;
1537  pScenLoader = nullptr;
1538  UpdateList(); // must clear scenario list, because it points to deleted stuff
1539  UpdateSelection(); // must clear picture facet of selection!
1540  }
1541  // dlg abort: return to main screen
1542  if (!fOK)
1543  {
1544  // clear settings: Password
1545  ::Network.SetPassword(nullptr);
1547  }
1548 }
1549 
1550 void C4StartupScenSelDlg::UpdateList()
1551 {
1552  AbortRenaming();
1553  // default: Show book (also for loading screen)
1554  pMapData = nullptr;
1555  pScenSelStyleTabular->SelectSheet(ShowStyle_Book, false);
1556  // and delete any stuff from map selection
1557  C4GUI::Tabular::Sheet *pMapSheet = pScenSelStyleTabular->GetSheet(ShowStyle_Map);
1558  while (pMapSheet->GetFirst()) delete pMapSheet->GetFirst();
1559  pfctBackground = nullptr;
1560  // for now, all the list is loaded at once anyway
1561  // so just clear and add all loaded items
1562  // remember old selection
1563  C4ScenarioListLoader::Entry *pOldSelection = GetSelectedEntry();
1564  C4GUI::Element *pEl;
1565  while ((pEl = pScenSelList->GetFirst())) delete pEl;
1566  pScenSelCaption->SetText("");
1567  // scen loader still busy: Nothing to add
1568  if (!pScenLoader) return;
1569  if (pScenLoader->IsLoading())
1570  {
1571  StdStrBuf sProgressText;
1572  sProgressText.Format(LoadResStr("IDS_MSG_SCENARIODESC_LOADING"), (int32_t) pScenLoader->GetProgressPercent());
1573  pScenSelProgressLabel->SetText(sProgressText.getData());
1574  pScenSelProgressLabel->SetVisibility(true);
1575  return;
1576  }
1577  pScenSelProgressLabel->SetVisibility(false);
1578  // is this a map folder? Then show the map instead
1579  C4ScenarioListLoader::Folder *pFolder = static_cast<C4ScenarioListLoader::Folder *>(pScenLoader->GetCurrFolder());
1580  if ((pMapData = pFolder->GetMapData()))
1581  {
1582  pMapData->ResetSelection();
1583  pMapData->CreateGUIElements(this, *pScenSelStyleTabular->GetSheet(ShowStyle_Map));
1584  pScenSelStyleTabular->SelectSheet(ShowStyle_Map, false);
1585  }
1586  else
1587  {
1588  // book style selection
1589  // add what has been loaded
1590  for (C4ScenarioListLoader::Entry *pEnt = pScenLoader->GetFirstEntry(); pEnt; pEnt = pEnt->GetNext())
1591  {
1592  if (pEnt->IsHidden()) continue; // no UI entry at all for hidden items
1593  ScenListItem *pEntItem = new ScenListItem(pScenSelList, pEnt);
1594  if (pEnt == pOldSelection) pScenSelList->SelectEntry(pEntItem, false);
1595  }
1596  // set title of current folder
1597  // but not root
1598  if (pFolder && pFolder != pScenLoader->GetRootFolder())
1599  pScenSelCaption->SetText(pFolder->GetName().getData());
1600  else
1601  {
1602  // special root title
1603  pScenSelCaption->SetText(LoadResStr("IDS_DLG_SCENARIOS"));
1604  }
1605  // new list has been loaded: Select first entry if nothing else had been selected
1606  if (!pOldSelection) pScenSelList->SelectFirstEntry(false);
1607  }
1608 }
1609 
1610 void C4StartupScenSelDlg::ResortFolder()
1611 {
1612  // if it's still loading, sorting will be done in the end anyway
1613  if (!pScenLoader || pScenLoader->IsLoading()) return;
1614  C4ScenarioListLoader::Folder *pFolder = pScenLoader->GetCurrFolder();
1615  if (!pFolder) return;
1616  pFolder->Resort();
1617  UpdateList();
1618 }
1619 
1620 void C4StartupScenSelDlg::UpdateSelection()
1621 {
1622  AbortRenaming();
1623  if (!pScenLoader)
1624  {
1625  C4Facet fctNoPic;
1626  pSelectionInfo->SetPicture(fctNoPic);
1627  pSelectionInfo->ClearText(false);
1628  SetOpenButtonDefaultText();
1629  return;
1630  }
1631  // determine target text box
1632  C4GUI::TextWindow *pSelectionInfo = pMapData ? pMapData->GetSelectionInfoBox() : this->pSelectionInfo;
1633  // get selected entry
1634  C4ScenarioListLoader::Entry *pSel = GetSelectedEntry();
1635  if (!pSel)
1636  {
1637  // no selection: Display data of current parent folder
1638  pSel = pScenLoader->GetCurrFolder();
1639  // but not root
1640  if (pSel == pScenLoader->GetRootFolder()) pSel = nullptr;
1641  }
1642  // get title image and desc of selected entry
1643  C4Facet fctTitle; StdStrBuf sTitle, sDesc, sVersion, sAuthor;
1644  if (pSel)
1645  {
1646  pScenLoader->LoadExtended(pSel); // 2do: Multithreaded
1647  fctTitle = pSel->GetTitlePicture();
1648  sTitle.Ref(pSel->GetName());
1649  sDesc.Ref(pSel->GetDesc());
1650  sVersion.Ref(pSel->GetVersion());
1651  sAuthor.Ref(pSel->GetAuthor());
1652  // never show a pure title string: There must always be some text or an image
1653  if (!fctTitle.Surface && (!sDesc || !*sDesc.getData()))
1654  sTitle.Clear();
1655  // selection specific open/start button
1656  pOpenBtn->SetText(pSel->GetOpenText().getData());
1657  pOpenBtn->SetToolTip(pSel->GetOpenTooltip().getData());
1658  }
1659  else
1660  SetOpenButtonDefaultText();
1661  // set data in selection component
1662  pSelectionInfo->ClearText(false);
1663  pSelectionInfo->SetPicture(fctTitle);
1664  if (sTitle && (!sDesc || !*sDesc.getData())) pSelectionInfo->AddTextLine(sTitle.getData(), &C4Startup::Get()->Graphics.BookFontCapt, ClrScenarioItem, false, false);
1665  if (sDesc) pSelectionInfo->AddTextLine(sDesc.getData(), &C4Startup::Get()->Graphics.BookFont, ClrScenarioItem, false, false, &C4Startup::Get()->Graphics.BookFontCapt);
1666  if (sAuthor) pSelectionInfo->AddTextLine(FormatString(LoadResStr("IDS_CTL_AUTHOR"), sAuthor.getData()).getData(),
1668  if (sVersion) pSelectionInfo->AddTextLine(FormatString(LoadResStr("IDS_DLG_VERSION"), sVersion.getData()).getData(),
1670  // update custom scenario options panel
1671  if (pSel)
1672  {
1673  pSelectionOptions->SetParameters(pSel->GetParameterDefs(), pSel->GetParameters());
1674  pSelectionOptions->Update();
1675  }
1676  else
1677  pSelectionOptions->SetParameters(nullptr, nullptr);
1678  // update component heights
1679  C4Rect rcSelBounds = pSelectionInfo->GetBounds();
1680  int32_t ymax = pSelectionOptions->GetBounds().GetBottom();
1681  C4GUI::Element *pLastOption = pSelectionOptions->GetLast();
1682  if (pLastOption)
1683  {
1684  // custom options present: Info box reduced; options box at bottom
1685  // set options box max size to 1/3rd of selection info area
1686  int32_t options_hgt = std::min<int32_t>(pLastOption->GetBounds().GetBottom() + pSelectionOptions->GetMarginTop() + pSelectionOptions->GetMarginTop(), rcSelBounds.Hgt/3);
1687  rcSelBounds.Hgt = ymax - options_hgt - rcSelBounds.y;
1688  pSelectionInfo->SetBounds(rcSelBounds);
1689  rcSelBounds.y = ymax - options_hgt;
1690  rcSelBounds.Hgt = options_hgt;
1691  pSelectionOptions->SetBounds(rcSelBounds);
1692  pSelectionOptions->SetVisibility(true);
1693  }
1694  else
1695  {
1696  // custom options absent: Info takes full area
1697  pSelectionOptions->SetVisibility(false);
1698  rcSelBounds.Hgt = ymax - rcSelBounds.y;
1699  pSelectionInfo->SetBounds(rcSelBounds);
1700  }
1701  pSelectionInfo->UpdateHeight();
1702 }
1703 
1704 C4StartupScenSelDlg::ScenListItem *C4StartupScenSelDlg::GetSelectedItem()
1705 {
1706  return static_cast<ScenListItem *>(pScenSelList->GetSelectedItem());
1707 }
1708 
1709 C4ScenarioListLoader::Entry *C4StartupScenSelDlg::GetSelectedEntry()
1710 {
1711  // map layout: Get selection from map
1712  if (pMapData) return pMapData->GetSelectedEntry();
1713  // get selection in listbox
1714  ScenListItem *pSel = static_cast<ScenListItem *>(pScenSelList->GetSelectedItem());
1715  return pSel ? pSel->GetEntry() : nullptr;
1716 }
1717 
1719 {
1720  assert(pStartScen);
1721  if (!pStartScen) return false;
1722  // get required object definitions
1723  if (pStartScen->GetC4S().Definitions.AllowUserChange)
1724  {
1725  // get definitions as user selects them
1726  StdStrBuf sDefinitions;
1727  if (!pStartScen->GetC4S().Definitions.GetModules(&sDefinitions)) sDefinitions.Copy("Objects.ocd");
1728  if (!C4DefinitionSelDlg::SelectDefinitions(GetScreen(), &sDefinitions))
1729  // user aborted during definition selection
1730  return false;
1731  SCopy(sDefinitions.getData(), ::Game.DefinitionFilenames, (sizeof Game.DefinitionFilenames)-1);
1732  }
1733  else
1734  // for no user change, just set default objects. Custom settings will override later anyway
1735  SCopy("Objects.ocd", ::Game.DefinitionFilenames);
1736  // set other default startup parameters
1737  ::Game.fLobby = !!::Game.NetworkActive; // always lobby in network
1738  ::Game.fObserve = false;
1739  C4ScenarioParameters *custom_params = pStartScen->GetParameters();
1740  if (custom_params) ::Game.StartupScenarioParameters = *custom_params; else ::Game.StartupScenarioParameters.Clear();
1741  // start with this set!
1743  return true;
1744 }
1745 
1747 {
1748  // open it through loader
1749  if (!pScenLoader) return false;
1750  bool fSuccess = pScenLoader->Load(pNewFolder, false);
1751  UpdateList();
1752  UpdateSelection();
1753  if (!pMapData) SetFocus(pScenSelList, false);
1754  return fSuccess;
1755 }
1756 
1758 {
1759  AbortRenaming();
1760  // get selected entry
1761  C4ScenarioListLoader::Entry *pSel = GetSelectedEntry();
1762  if (!pSel) return false;
1763  // check if open is possible
1764  StdStrBuf sError;
1765  if (!pSel->CanOpen(sError))
1766  {
1767  GetScreen()->ShowMessage(sError.getData(), LoadResStr("IDS_MSG_CANNOTSTARTSCENARIO"), C4GUI::Ico_Error);
1768  return false;
1769  }
1770  // if CanOpen returned true but set an error message, that means it's a warning. Display it!
1771  if (sError.getLength())
1772  {
1774  // user chose to not start it
1775  return false;
1776  }
1777  // start it!
1778  return pSel->Start();
1779 }
1780 
1781 bool C4StartupScenSelDlg::DoBack(bool fAllowClose)
1782 {
1783  AbortRenaming();
1784  // if in a subfolder, try backtrace folders first
1785  if (pScenLoader && pScenLoader->FolderBack())
1786  {
1787  UpdateList();
1788  UpdateSelection();
1789  return true;
1790  }
1791  // while this isn't multithreaded, the dialog must not be aborted while initial load...
1792  if (pScenLoader && pScenLoader->IsLoading()) return false;
1793  // return to main screen
1794  if (fAllowClose)
1795  {
1796  Close(false);
1797  return true;
1798  }
1799  return false;
1800 }
1801 
1803 {
1804  if (pScenLoader && !pScenLoader->IsLoading())
1805  {
1806  pScenSelList->SelectNone(false);
1807  pScenLoader->ReloadCurrent();
1808  UpdateList();
1809  UpdateSelection();
1810  }
1811 }
1812 
1813 void C4StartupScenSelDlg::SetOpenButtonDefaultText()
1814 {
1815  pOpenBtn->SetText(LoadResStr("IDS_BTN_OPEN"));
1816  pOpenBtn->SetToolTip(LoadResStr("IDS_DLGTIP_SCENSELNEXT"));
1817 }
1818 
1820 {
1821  // no rename in map mode
1822  if (pMapData) return false;
1823  // not if renaming already
1824  if (IsRenaming()) return false;
1825  // forward to selected scenario list item
1826  ScenListItem *pSel = GetSelectedItem();
1827  if (!pSel) return false;
1828  return pSel->KeyRename();
1829 }
1830 
1832 {
1833  // do not delete from map folder
1834  if (pMapData) return false;
1835  // cancel renaming
1836  AbortRenaming();
1837  // delete selected item: Confirmation first
1838  ScenListItem *pSel = GetSelectedItem();
1839  if (!pSel) return false;
1840  C4ScenarioListLoader::Entry *pEnt = pSel->GetEntry();
1841  StdStrBuf sWarning;
1842  sWarning.Format(LoadResStr("IDS_MSG_PROMPTDELETE"), FormatString("%s %s", pEnt->GetTypeName().getData(), pEnt->GetName().getData()).getData(), pEnt->GetEntryFilename().getData());
1843  GetScreen()->ShowRemoveDlg(new C4GUI::ConfirmationDialog(sWarning.getData(), LoadResStr("IDS_MNU_DELETE"),
1845  return true;
1846 }
1847 
1849 {
1850  // deletion confirmed. Do it.
1851  C4ScenarioListLoader::Entry *pEnt = pSel->GetEntry();
1852  if (!C4Group_DeleteItem(pEnt->GetEntryFilename().getData(), true))
1853  {
1854  StdStrBuf sMsg; sMsg.Format("%s", LoadResStr("IDS_FAIL_DELETE"));
1856  return;
1857  }
1858  // remove from scenario list
1859  pScenSelList->SelectEntry(pSel->GetNext(), false);
1860  delete pEnt;
1861  delete pSel;
1862 }
1863 
1865 {
1866  return ::pGUI->ShowRemoveDlg(new C4GUI::InputDialog(LoadResStr("IDS_TEXT_ENTERMISSIONPASSWORD"), LoadResStr("IDS_DLG_MISSIONACCESS"), C4GUI::Ico_Options,
1868  false));
1869 }
1870 
1872 {
1873  // Special character "-": remove mission password(s)
1874  if (SEqual2(rsCheatCode.getData(), "-"))
1875  {
1876  const char *szPass = rsCheatCode.getPtr(1);
1877  if (szPass && *szPass)
1878  {
1879  SRemoveModules(Config.General.MissionAccess, szPass, false);
1880  UpdateList();
1881  UpdateSelection();
1882  return;
1883  }
1884  }
1885 
1886  // No special character: add mission password(s)
1887  const char *szPass = rsCheatCode.getPtr(0);
1888  if (szPass && *szPass)
1889  {
1890  SAddModules(Config.General.MissionAccess, szPass, false);
1891  UpdateList();
1892  UpdateSelection();
1893  return;
1894  }
1895 
1896 }
1897 
1898 void C4StartupScenSelDlg::FocusScenList()
1899 {
1900  SetFocus(pScenSelList, false);
1901 }
1902 
1904 {
1905  // map button was clicked: Update selected scenario
1906  if (!pMapData || !pEl) return;
1907  C4ScenarioListLoader::Entry *pSel = GetSelectedEntry(), *pSel2;
1908  pMapData->OnButtonScenario(pEl);
1909  pSel2 = GetSelectedEntry();
1910  if (pSel && pSel==pSel2)
1911  {
1912  // clicking on the selected scenario again starts it
1913  DoOK();
1914  return;
1915  }
1916  // the first click selects it
1917  SetFocus(pEl, false);
1918  UpdateSelection();
1919 }
1920 
1922 {
1923  // Deselect all so current folder info is displayed
1924  if (GetFocus()) C4GUI::GUISound("UI::Tick");
1925  SetFocus(nullptr, true);
1926  if (pMapData) pMapData->ResetSelection();
1927  UpdateSelection();
1928 }
1929 
1931 {
1932  pRenameEdit = pNewRenameEdit;
1933 }
1934 
1936 {
1937  if (pRenameEdit) pRenameEdit->Abort();
1938 }
1939 
1940 void C4StartupScenSelDlg::UpdateAchievements()
1941 {
1942  // Extract all achievements from activated player files and merge them
1943  Achievements.Clear();
1944  char PlayerFilename[_MAX_FNAME+1];
1945  C4Group PlayerGrp;
1946  for (int i = 0; SCopySegment(Config.General.Participants, i, PlayerFilename, ';', _MAX_FNAME, true); i++)
1947  {
1948  const char *szPlayerFilename = Config.AtUserDataPath(PlayerFilename);
1949  if (!FileExists(szPlayerFilename)) continue;
1950  if (!PlayerGrp.Open(szPlayerFilename)) continue;
1951  C4PlayerInfoCore nfo;
1952  if (!nfo.Load(PlayerGrp)) continue;
1953  Achievements.Merge(nfo.Achievements);
1954  }
1955 }
1956 
1958 {
1959  if (pSelectionOptions) pSelectionOptions->Update();
1960 }
1961 
1962 // NICHT: 9, 7.2.2, 113-114, 8a
1963 
char * GetFilename(char *szPath)
Definition: StdFile.cpp:45
const char * getData() const
Definition: StdBuf.h:442
void MouseInput(C4GUI::CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam) override
bool FindEntry(const char *szWildCard, StdStrBuf *sFileName=nullptr, size_t *iSize=nullptr)
Definition: C4Group.cpp:1774
int32_t GetHeight() const
Definition: C4Gui.h:2801
#define C4CFN_WriteTitle
Definition: C4Components.h:83
void SetText(const char *szToText)
Definition: C4GuiButton.cpp:55
void SetPassword(const char *szToPassword)
Definition: C4Network2.cpp:771
void OpenGame(const char *scenario=nullptr)
CStdFont BookFontTitle
Definition: C4Startup.h:65
int32_t AlphabeticalSorting
Definition: C4Config.h:189
const int32_t C4StartupScenSel_DefaultIcon_WinFolder
#define C4CFN_FolderFiles
Definition: C4Components.h:175
const int32_t C4StartupScenSel_TitlePictureWdt
int32_t GetProgressPercent() const
C4Config Config
Definition: C4Config.cpp:833
char * GrabPointer()
Definition: StdBuf.h:459
const int32_t C4MC_Button_LeftDown
void OnClosed(bool fOK) override
int GetLineHeight() const
Definition: C4FontLoader.h:125
void GUISound(const char *szSound)
Definition: C4Gui.cpp:1175
C4ConfigStartup Startup
Definition: C4Config.h:260
Element * GetLast()
Definition: C4Gui.h:1572
#define C4GUI_ButtonHgt
Definition: C4Gui.h:111
void SCopy(const char *szSource, char *sTarget, size_t iMaxL)
Definition: Standard.cpp:130
bool LoadExtended(Entry *pEntry)
C4SDefinitions Definitions
Definition: C4Scenario.h:231
void OnSelChange(class C4GUI::Element *pEl)
void SetDecoration(bool fDrawBG, bool fDrawFrame, ScrollBarFacets *pToGfx, bool fAutoScroll)
Definition: C4Gui.h:1752
int Replace(const char *szOld, const char *szNew, size_t iStartSearch=0)
Definition: StdBuf.cpp:284
StdNamingCountAdapt< int_t > mkNamingCountAdapt(int_t &iCount, const char *szName)
Definition: StdAdaptors.h:976
void StartRenaming(C4GUI::RenameEdit *pNewRenameEdit)
bool Load(C4Group &hGroup, const char *szFilename, const char *szLanguage=nullptr)
int Wdt
Definition: C4Surface.h:65
void SAppend(const char *szSource, char *szTarget, int iMaxL)
Definition: Standard.cpp:235
bool ItemIdentical(const char *szFilename1, const char *szFilename2)
Definition: StdFile.cpp:855
C4Game Game
Definition: C4Globals.cpp:52
char MissionAccess[CFG_MaxString+1]
Definition: C4Config.h:45
float bottom
Definition: C4Rect.h:25
const int32_t C4StartupScenSel_DefaultIcon_OldIconBG
void Clear()
Definition: StdBuf.h:466
C4ConfigGeneral General
Definition: C4Config.h:251
C4GUI::TextWindow * GetSelectionInfoBox() const
bool Load(C4Group &hGroup, const char *szName, int iWdt, int iHgt, bool fNoErrIfNotFound, int iFlags)
Definition: C4FacetEx.cpp:78
float right
Definition: C4Rect.h:25
const char * GetData() const
virtual void OnShown()
Definition: C4Gui.h:2208
bool SCopySegment(const char *szString, int iSegment, char *sTarget, char cSeparator, int iMaxL, bool fSkipWhitespace)
Definition: Standard.cpp:251
int stricmp(const char *s1, const char *s2)
bool Lock()
Definition: C4Surface.cpp:453
float left
Definition: C4Rect.h:25
const uint32_t ClrScenarioItemDisabled
bool SavePNG(C4Group &hGroup, const char *szFilename, bool fSaveAlpha=true, bool fSaveOverlayOnly=false)
void DrawBackground(C4TargetFacet &cgo, C4Facet &rFromFct)
bool StartScenario(C4ScenarioListLoader::Scenario *pStartScen)
void SetFacet(const C4Facet &fct)
Definition: C4Gui.h:612
void SetToolTip(const char *szNewTooltip, bool is_immediate=false)
Definition: C4Gui.cpp:409
C4GUI::RenameEdit::RenameResult DoRenaming(RenameParams par, const char *szNewName)
static bool SelectDefinitions(C4GUI::Screen *pOnScreen, StdStrBuf *pSelection)
void OnSelDblClick(class C4GUI::Element *pEl)
C4ScenarioListLoader(const C4ScenarioParameters &Achievements)
bool InsertElement(Element *pChild, Element *pInsertBefore, int32_t iIndent=0)
void SetSheetMargin(int32_t iMargin)
Definition: C4Gui.h:1713
bool SEqualNoCase(const char *szStr1, const char *szStr2, int iLen)
Definition: Standard.cpp:185
bool Unlock()
Definition: C4Surface.cpp:464
CStdFont & GetFontByHeight(int32_t iHgt, float *pfZoom=nullptr)
void SetBackground(C4Facet *pNewBG)
void Close(bool fOK)
const int C4FCT_Height
Definition: C4FacetEx.h:27
bool ShowRemoveDlg(Dialog *pDlg)
#define C4CFN_ScenarioParameterDefs
Definition: C4Components.h:43
Definition: C4Rect.h:27
int32_t GetScreenWdt()
Definition: C4Gui.h:2821
Entry * pNext
bool GetTextExtent(const char *szText, int32_t &rsx, int32_t &rsy, bool fCheckMarkup=true)
void OnButtonScenario(C4GUI::Control *pEl)
int GetFolderIndex()
#define C4CFN_Version
Definition: C4Components.h:72
void Format(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
Definition: StdBuf.cpp:174
void DrawElement(C4TargetFacet &cgo) override
const C4Scenario & GetC4S() const
C4ScenarioParameters * GetParameters() override
void AddElement(Element *pChild)
const StdStrBuf & GetAuthor() const
bool SIsModule(const char *szList, const char *szString, int *ipIndex, bool fCaseSensitive)
Definition: Standard.cpp:519
bool GetModules(StdStrBuf *psOutModules) const
Definition: C4Scenario.cpp:420
const uint32_t ClrScenarioItem
Element * GetFirst()
Definition: C4Gui.h:1571
bool Delete(const char *szFiles, bool fRecursive=false)
Definition: C4Group.cpp:1334
#define _MAX_PATH
#define C4CFN_FolderCore
Definition: C4Components.h:44
void SetSelectionChangeCallbackFn(BaseCallbackHandler *pToHandler)
Definition: C4Gui.h:1548
virtual StdStrBuf GetOpenText()=0
C4Rect GetContainedClientRect()
Definition: C4Gui.h:448
C4GraphicsResource GraphicsResource
void SetDecoration(bool fDrawBG, ScrollBarFacets *pToGfx, bool fAutoScroll, bool fDrawBorder=false)
Definition: C4Gui.h:1566
void SelectSheet(int32_t iIndex, bool fByUser)
bool SEqual(const char *szStr1, const char *szStr2)
Definition: Standard.h:93
void Set(const C4Facet &cpy)
Definition: C4FacetEx.h:46
C4FacetID fctScenSelIcons
Definition: C4Startup.h:61
virtual ~Entry()
bool LoadCustom(C4Group &rGrp, bool fNameLoaded, bool fIconLoaded) override
bool IsShown()
Definition: C4Gui.h:2147
const StdStrBuf & GetVersion() const
bool LoadCustomPre(C4Group &rGrp) override
void CreateGUIElements(C4StartupScenSelDlg *pMainDlg, C4GUI::Window &rContainer)
const char * LoadResStr(const char *id)
Definition: C4Language.h:83
bool SAddModules(char *szList, const char *szModules, bool fCaseSensitive)
Definition: Standard.cpp:548
Element * GetSelectedItem()
Definition: C4Gui.h:1580
const StdStrBuf & GetDesc() const
bool SRemoveModules(char *szList, const char *szModules, bool fCaseSensitive)
Definition: Standard.cpp:577
void MakeFilenameFromTitle(char *szTitle)
Definition: StdFile.cpp:402
C4StartupScenSelDlg(bool fNetwork)
int iDifficulty
void KeyCheat2(const StdStrBuf &rsCheatCode)
StdNamingAdapt< T > mkNamingAdapt(T &&rValue, const char *szName)
Definition: StdAdaptors.h:92
bool GetLanguageString(const char *szLanguage, StdStrBuf &rTarget)
static StdStrBuf AddFilename2ID(const char *filename, const char *id)
#define C4CFN_ScriptStringTbl
Definition: C4Components.h:68
int32_t GetMiddleX() const
Definition: C4Rect.h:56
int32_t Wdt
Definition: C4Rect.h:30
static bool LoadComponentHost(C4ComponentHost *host, C4Group &hGroup, const char *szFilename, const char *szLanguage)
Definition: C4Language.cpp:232
#define C4CFN_MapFolderBG
Definition: C4Components.h:136
std::vector< C4KeyCodeEx > CodeList
const int32_t C4StartupScenSel_TitlePictureHgt
C4GUIScreen * pGUI
Definition: C4Gui.cpp:1191
#define C4CFN_Title
Definition: C4Components.h:82
bool DoLoadContents(C4ScenarioListLoader *pLoader, C4Group *pFromGrp, const StdStrBuf &sFilename, bool fLoadEx) override
void SelectFirstEntry(bool fByUser)
Definition: C4Gui.h:1584
void SetAutosize(bool fToVal)
Definition: C4Gui.h:507
virtual void SetVisibility(bool fToValue)
Definition: C4Gui.cpp:207
bool DebugLogF(const char *strMessage...)
Definition: C4Log.cpp:278
CStdFont BookSmallFont
Definition: C4Startup.h:65
C4Network2 Network
Definition: C4Globals.cpp:53
const int32_t C4StartupScenSel_TitleOverlayMargin
bool Load(C4Group &hGroup)
Definition: C4InfoCore.cpp:78
const char * LoadResStrNoAmp(const char *id)
Definition: StdResStr2.cpp:23
int32_t y
Definition: C4Rect.h:30
void CompileFunc(StdCompiler *pComp)
bool DoProcessCallback(int32_t iProgress, int32_t iMaxProgress)
char Participants[CFG_MaxString+1]
Definition: C4Config.h:38
bool ShowMessageModal(const char *szMessage, const char *szCaption, DWORD dwButtons, Icons icoIcon, int32_t *piConfigDontShowAgainSetting=nullptr)
Element * GetFirst()
Definition: C4Gui.h:829
void OnButtonScenario(C4GUI::Control *pEl)
const int ACenter
Definition: C4Surface.h:41
bool CanOpen(StdStrBuf &sError) override
bool SetPixDw(int iX, int iY, DWORD dwCol)
Definition: C4Surface.cpp:576
const char * GetName() const
Definition: C4Group.cpp:1845
StdStrBuf GetFullName() const
Definition: C4Group.cpp:2078
C4FacetID fctOptionsIcons
Definition: C4Startup.h:71
int32_t GetMarginTop() override
Definition: C4Gui.h:1575
bool Open(const char *szGroupName, bool fCreate=false)
Definition: C4Group.cpp:514
void SetGfx(C4Facet *pafctBack, C4Facet *pafctClip, C4Facet *pafctIcons, CStdFont *paSheetCaptionFont, bool fResizeByAspect)
C4Rect rcBounds
Definition: C4Gui.h:385
bool GetAchievement(int32_t idx, C4Facet *out_facet, const char **out_description) override
void Take(char *pnData)
Definition: StdBuf.h:457
int iIconIndex
int32_t PictureWidth
C4Rect & GetClientRect() override
Definition: C4Gui.h:864
StdPtrAdapt< T > mkPtrAdaptNoNull(T *&rpObj)
Definition: StdAdaptors.h:606
virtual StdStrBuf GetTypeName()=0
void Draw(C4Facet &cgo, bool fAspect=true, int32_t iPhaseX=0, int32_t iPhaseY=0, bool fTransparent=true)
Definition: C4Facet.cpp:154
void UpdateHeight()
Definition: C4Gui.h:1750
bool AllowUserChange
Definition: C4Scenario.h:93
bool C4Group_IsGroup(const char *szFilename)
Definition: C4Group.cpp:94
class C4StartupDlg * SwitchDialog(DialogID eToDlg, bool fFade=true, const char *szSubDialog=nullptr)
Definition: C4Startup.cpp:137
const int32_t C4StartupScenSel_DefaultIcon_SavegamesFolder
bool DirContainsScenarios(const char *szDir)
bool Close()
Definition: C4Group.cpp:755
C4Draw * pDraw
Definition: C4Draw.cpp:42
int SModuleCount(const char *szList)
Definition: Standard.cpp:589
virtual bool SetTitleInGroup(C4Group &rGrp, const char *szNewTitle)
Sheet * GetSheet(int32_t iIndex)
Definition: C4Gui.h:1706
void ReplaceStrings(StdStrBuf &rBuf)
const int32_t C4StartupScenSel_TitlePicturePadding
void UpdateSize() override
static Entry * CreateEntryForFile(const StdStrBuf &sFilename, C4ScenarioListLoader *pLoader, Folder *pParent)
int32_t HideMsgStartDedicated
Definition: C4Config.h:184
#define _MAX_FNAME
bool DoLoadContents(C4ScenarioListLoader *pLoader, C4Group *pFromGrp, const StdStrBuf &sFilename, bool fLoadEx) override
char * GetExtension(char *szFilename)
Definition: StdFile.cpp:121
Folder * GetRootFolder() const
bool NetworkActive
Definition: C4Game.h:122
void Value(const T &rStruct)
Definition: StdCompiler.h:161
DWORD GetPixDw(int iX, int iY, bool fApplyModulation)
Definition: C4Surface.cpp:491
const char * GetID() const
char LanguageEx[CFG_MaxString+1]
Definition: C4Config.h:37
virtual Folder * GetIsFolder()
Entry(class C4ScenarioListLoader *pLoader, class Folder *pParent)
int32_t GetScreenHgt()
Definition: C4Gui.h:2822
bool DoBack(bool fAllowClose)
CStdFont BookFont
Definition: C4Startup.h:65
virtual StdStrBuf GetOpenTooltip()=0
void RemoveExtension(char *szFilename)
Definition: StdFile.cpp:306
C4Rect & GetBounds()
Definition: C4Gui.h:445
int32_t GetWidth() const
Definition: C4Gui.h:2800
char Language[CFG_MaxString+1]
Definition: C4Config.h:36
C4Reloc Reloc
Definition: C4Reloc.cpp:21
bool fObserve
Definition: C4Game.h:120
#define C4CFN_Achievements
Definition: C4Components.h:90
const char * GetError()
Definition: C4Group.cpp:504
void OnNextBtn(C4GUI::Control *btn)
virtual bool isDeserializer()
Definition: StdCompiler.h:53
void SelectEntry(Element *pNewSel, bool fByUser)
int32_t x
Definition: C4Rect.h:30
bool OpenFolder(C4ScenarioListLoader::Folder *pNewFolder)
bool FileExists(const char *szFileName)
int PreCacheEntries(const char *szSearchPattern, bool cache_previous=false)
Definition: C4Group.cpp:2222
void SelectNone(bool fByUser)
Definition: C4Gui.h:1585
void Set(int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt)
Definition: C4Rect.cpp:86
bool ShowMessage(const char *szMessage, const char *szCaption, Icons icoIcon, int32_t *piConfigDontShowAgainSetting=nullptr)
bool Load(const StdStrBuf &sRootFolder)
void Ref(const char *pnData)
Definition: StdBuf.h:455
bool C4Group_MoveItem(const char *szSource, const char *szTarget1, bool fNoSort)
Definition: C4Group.cpp:144
bool DirectoryExists(const char *szFilename)
Definition: StdFile.cpp:684
bool Blit(C4Surface *sfcSource, float fx, float fy, float fwdt, float fhgt, C4Surface *sfcTarget, float tx, float ty, float twdt, float thgt, bool fSrcColKey=false, const C4BltTransform *pTransform=nullptr)
Definition: C4Draw.cpp:301
const Option * GetOptionByValue(int32_t val) const
#define C4CFN_MapFolderData
Definition: C4Components.h:135
float TargetX
Definition: C4Facet.h:165
Entry * GetFirstEntry() const
void SetFocus(Control *pCtrl, bool fByMouse)
virtual C4ScenarioParameters * GetParameters()
const int32_t C4StartupScenSel_DefaultIcon_Folder
bool LoadEntryString(const char *szEntryName, StdStrBuf *Buf)
Definition: C4Group.cpp:1932
class C4ScenarioParameters & StartupScenarioParameters
Definition: C4Game.h:68
virtual bool IsGrayed()
bool FindNextEntry(const char *szWildCard, StdStrBuf *sFileName=nullptr, size_t *iSize=nullptr, bool fStartAtFilename=false)
Definition: C4Group.cpp:1780
#define C4CFN_IconPNG
Definition: C4Components.h:86
float Hgt
Definition: C4Facet.h:118
void SetBounds(const C4Rect &rcNewBound)
Definition: C4Gui.h:446
void Merge(const C4ScenarioParameters &other)
#define C4CFN_ScenarioCore
Definition: C4Components.h:42
class Folder * GetParent() const
void ClientPos2ScreenPos(int32_t &riX, int32_t &riY)
Definition: C4Gui.cpp:231
void SetVisibility(bool fToValue) override
#define C4CFN_ScenarioDesc
Definition: C4Components.h:88
C4FacetSurface * FindByName(const char *name) const
bool Create(int iWdt, int iHgt, int iWdt2=C4FCT_Full, int iHgt2=C4FCT_Full)
Definition: C4FacetEx.cpp:54
bool LoadContents(C4ScenarioListLoader *pLoader, C4Group *pFromGrp, const StdStrBuf *psFilename, bool fLoadEx, bool fReload)
const char * AtUserDataPath(const char *szFilename)
Definition: C4Config.cpp:526
const C4Facet & GetTitlePicture() const
const int ALeft
Definition: C4Surface.h:41
bool LoadCustomPre(C4Group &rGrp) override
bool WildcardMatch(const char *szWildcard, const char *szString)
Definition: StdFile.cpp:374
C4AchievementGraphics Achievements
ScenListItem(C4GUI::ListBox *pForListBox, C4ScenarioListLoader::Entry *pForEntry, C4GUI::Element *pInsertBeforeElement=nullptr)
bool SEqual2(const char *szStr1, const char *szStr2)
Definition: Standard.cpp:176
const char * GetAchievement() const
#define C4CFN_AnyScriptStringTbl
Definition: C4Components.h:69
static C4Startup * Get()
Definition: C4Startup.h:133
void OnBackBtn(C4GUI::Control *btn)
const char * getPtr(size_t i) const
Definition: StdBuf.h:448
bool Add(const char *szFile, const char *szAddAs)
Definition: C4Group.cpp:1316
char DefinitionFilenames[20 *_MAX_PATH+1]
Definition: C4Game.h:105
bool IsNetworkStart() const
const StdStrBuf & GetName() const
CStdFont & GetBlackFontByHeight(int32_t iHgt, float *pfZoom)
Definition: C4Startup.cpp:96
#define C4CFN_ScenarioFiles
Definition: C4Components.h:174
bool LoadCustom(C4Group &rGrp, bool fNameLoaded, bool fIconLoaded) override
void AddTextLine(const char *szText, CStdFont *pFont, DWORD dwClr, bool fDoUpdate, bool fMakeReadableOnBlack, CStdFont *pCaptionFont=nullptr)
Definition: C4Gui.h:1740
int32_t GetBottom() const
Definition: C4Rect.h:58
StdParameterAdapt< T, P > mkParAdapt(T &&rObj, P &&rPar)
Definition: StdAdaptors.h:458
bool GetFromRight(int32_t iWdt, int32_t iHgt, C4Rect &rcOut)
Definition: C4Gui.cpp:1093
Sheet * AddSheet(const char *szTitle, int32_t icoTitle=Ico_None)
bool Load(C4Group *pFromGrp, const StdStrBuf *psFilename, bool fLoadEx)
size_t getLength() const
Definition: StdBuf.h:445
void ResetSearch(bool reload_contents=false)
Definition: C4Group.cpp:1013
void DeleteConfirm(ScenListItem *pSel)
virtual Screen * GetScreen()
Definition: C4Gui.cpp:289
bool C4Group_TestIgnore(const char *szFilename)
Definition: C4Group.cpp:85
int32_t Hgt
Definition: C4Rect.h:30
bool ScheduleProcs(int iTimeout=1000/36)
C4ScenarioListLoader::Entry * GetSelectedEntry() const
void SetPicture(const C4Facet &rNewPic)
C4Surface * Surface
Definition: C4Facet.h:117
#define DirSep
bool ItemExists(const char *szItemName)
Definition: StdFile.h:75
float TargetY
Definition: C4Facet.h:165
CStdFont BookFontCapt
Definition: C4Startup.h:65
bool C4Group_DeleteItem(const char *szItem, bool fRecycle)
Definition: C4Group.cpp:194
C4ScenarioListLoader::Entry * GetEntry() const
const int32_t C4StartupScenSel_MaxAchievements
#define C4CFN_Savegames
Definition: C4Components.h:35
Folder * GetCurrFolder() const
int GetDifficulty()
C4FacetID fctOptionsTabClip
Definition: C4Startup.h:71
uint32_t DWORD
bool CheckNameHotkey(const char *c) override
StdArrayAdapt< T, M > mkArrayAdaptMap(T *pArray, int iSize, M &&map)
Definition: StdAdaptors.h:313
virtual bool Start()=0
bool fLobby
Definition: C4Game.h:118
const int C4FCT_Full
Definition: C4FacetEx.h:26
Entry * FindEntryByName(const char *szFilename) const
#define C4CFN_ScenarioIcon
Definition: C4Components.h:85
int EntrySortFunc(const void *pEl1, const void *pEl2)
bool LoadCustom(C4Group &rGrp, bool fNameLoaded, bool fIconLoaded) override
void Copy()
Definition: StdBuf.h:467
float Wdt
Definition: C4Facet.h:118
C4Facet GetPhase(int iPhaseX=0, int iPhaseY=0)
Definition: C4Facet.cpp:59
C4ScenarioParameters Achievements
Definition: C4InfoCore.h:108
bool Inside(T ival, U lbound, V rbound)
Definition: Standard.h:43
C4StartupGraphics Graphics
Definition: C4Startup.h:100
int iFolderIndex
void Clear()
Definition: C4FacetEx.h:44
void SetParameters(C4ScenarioParameterDefs *param_defs, C4ScenarioParameters *params)
#define C4CFN_ScenarioTitle
Definition: C4Components.h:84
const C4Facet & GetIconFacet() const
const StdStrBuf & GetEntryFilename() const
C4Application Application
Definition: C4Globals.cpp:44
bool GetCentered(int32_t iWdt, int32_t iHgt, C4Rect &rcOut)
Definition: C4Gui.cpp:1133
void CompileFunc(C4Real &rValue, StdCompiler *pComp)
Definition: C4Real.cpp:9033
void OnLeagueOptionChanged() override
float top
Definition: C4Rect.h:25
bool GetFromBottom(int32_t iHgt, int32_t iWdt, C4Rect &rcOut)
Definition: C4Gui.cpp:1109
bool OpenAsChild(C4Group *pMother, const char *szEntryName, bool fExclusive=false, bool fCreate=false)
Definition: C4Group.cpp:1585
C4MapFolderData * GetMapData() const
virtual bool GetAchievement(int32_t idx, C4Facet *out_facet, const char **out_description)
virtual bool CanOpen(StdStrBuf &sError)
void SetText(const char *szToText, bool fAllowHotkey=true)
Definition: C4GuiLabels.cpp:74
int GetIconIndex()
const uint32_t ClrScenarioItemXtra
bool Load(C4Group &hGroup, C4ScenarioListLoader::Folder *pScenLoaderFolder)
void ClearText(bool fDoUpdate)
Definition: C4Gui.h:1744
Control * GetFocus()
Definition: C4Gui.h:2115
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:270
bool RenameTo(const char *szNewName)
virtual C4ScenarioParameterDefs * GetParameterDefs()
void SetSelectionDblClickFn(BaseCallbackHandler *pToHandler)
Definition: C4Gui.h:1553
static C4StartupScenSelDlg * pInstance
Entry * GetNext() const